#include <sys/param.h>
#include <sys/procfs.h>
#include <fcntl.h>
#include <stdio.h>
#include <libelf.h>
#include <link.h>
#include <dlfcn.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include "libld.h"
#include "msg.h"
#include "_librtld.h"
static void
cleanup(Elf *ielf, Elf *oelf, Elf *melf, Cache *icache, Cache *mcache,
int fd, const char *opath)
{
if (icache) {
Cache * _icache = icache;
for (++_icache; _icache->c_flags != FLG_C_END; _icache++) {
if (_icache->c_info)
(void) free(_icache->c_info);
}
(void) free((void *)icache);
}
if (mcache)
(void) free((void *)mcache);
if (ielf)
(void) elf_end(ielf);
if (oelf)
(void) elf_end(oelf);
if (melf)
(void) elf_end(melf);
if (fd)
(void) close(fd);
if (opath)
(void) unlink(opath);
}
int
rt_dldump(Rt_map *lmp, const char *opath, int flags, Addr addr)
{
Elf * ielf = 0, *oelf = 0, *melf = 0;
Ehdr *iehdr, *oehdr, *mehdr;
Phdr *iphdr, *ophdr, *data_phdr = 0;
Cache *icache = 0, *_icache, *mcache = 0, *_mcache;
Cache *data_cache = 0, *dyn_cache = 0;
Xword rel_null_no = 0, rel_data_no = 0, rel_func_no = 0;
Xword rel_entsize;
Rel *rel_base = 0, *rel_null, *rel_data, *rel_func;
Elf_Scn *scn;
Shdr *shdr;
Elf_Data *data;
Half endx = 1;
int fd = 0, err, num;
size_t shstr_size = 1, shndx;
Addr edata;
char *shstr, *_shstr, *ipath = NAME(lmp);
prstatus_t *status = 0, _status;
Lm_list *lml = LIST(lmp);
Alist *nodirect = 0;
if (lmp == lml_main.lm_head) {
char proc[16];
int pfd;
(void) snprintf(proc, 16, MSG_ORIG(MSG_FMT_PROC),
(int)getpid());
if ((pfd = open(proc, O_RDONLY)) == -1) {
err = errno;
eprintf(lml, ERR_FATAL, MSG_INTL(MSG_SYS_OPEN), proc,
strerror(err));
return (1);
}
if ((fd = ioctl(pfd, PIOCOPENM, (void *)0)) == -1) {
err = errno;
eprintf(lml, ERR_FATAL, MSG_INTL(MSG_SYS_PROC), ipath,
strerror(err));
(void) close(pfd);
return (1);
}
if (!(flags & RTLD_NOHEAP)) {
if (ioctl(pfd, PIOCSTATUS, (void *)&_status) == -1) {
err = errno;
eprintf(lml, ERR_FATAL, MSG_INTL(MSG_SYS_PROC),
ipath, strerror(err));
(void) close(fd);
(void) close(pfd);
return (1);
}
if ((flags & RTLD_MEMORY) && _status.pr_brksize)
status = &_status;
}
(void) close(pfd);
} else {
if ((fd = open(ipath, O_RDONLY, 0)) == -1) {
err = errno;
eprintf(lml, ERR_FATAL, MSG_INTL(MSG_SYS_OPEN), ipath,
strerror(err));
return (1);
}
}
(void) elf_version(EV_CURRENT);
if ((ielf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_BEGIN), ipath);
cleanup(ielf, oelf, melf, icache, mcache, fd, 0);
return (1);
}
(void) close(fd);
if ((elf_kind(ielf) != ELF_K_ELF) ||
((iehdr = elf_getehdr(ielf)) == NULL) ||
((iehdr->e_type != ET_EXEC) && (iehdr->e_type != ET_DYN))) {
eprintf(lml, ERR_FATAL, MSG_INTL(MSG_IMG_ELF), ipath);
cleanup(ielf, oelf, melf, icache, mcache, 0, 0);
return (1);
}
if ((fd = open(opath, (O_RDWR | O_CREAT | O_TRUNC), 0777)) == -1) {
err = errno;
eprintf(lml, ERR_FATAL, MSG_INTL(MSG_SYS_OPEN), opath,
strerror(err));
cleanup(ielf, oelf, melf, icache, mcache, 0, 0);
return (1);
}
if ((oelf = elf_begin(fd, ELF_C_WRITE, NULL)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_BEGIN), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
if ((iphdr = elf_getphdr(ielf)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_GETPHDR), ipath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
for (num = 0, ophdr = iphdr; num != iehdr->e_phnum; num++, ophdr++) {
if (ophdr->p_type == PT_LOAD) {
if (ophdr->p_filesz != ophdr->p_memsz)
data_phdr = ophdr;
else if (data_phdr) {
if (data_phdr->p_vaddr < ophdr->p_vaddr)
data_phdr = ophdr;
} else
data_phdr = ophdr;
}
}
if (status && !data_phdr) {
eprintf(lml, ERR_WARNING, MSG_INTL(MSG_IMG_DATASEG), ipath);
status = 0;
}
if (elf_getshdrstrndx(ielf, &shndx) == -1) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_GETSHDRSTRNDX), ipath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
if ((scn = elf_getscn(ielf, shndx)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_GETSCN), ipath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
if ((data = elf_getdata(scn, NULL)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_GETDATA), ipath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
shstr = (char *)data->d_buf;
if (elf_getshdrnum(ielf, &shndx) == -1) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_GETSHDRNUM), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
num = shndx;
if (status)
num++;
if ((icache = calloc(num + 1, sizeof (Cache))) == 0) {
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
icache[num].c_flags = FLG_C_END;
_icache = icache;
_icache++;
for (scn = 0; scn = elf_nextscn(ielf, scn); _icache++) {
if ((_icache->c_shdr = shdr = elf_getshdr(scn)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_GETSHDR), ipath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
if ((_icache->c_data = elf_getdata(scn, NULL)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_GETDATA), ipath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
_icache->c_name = shstr + (size_t)(shdr->sh_name);
_icache->c_scn = scn;
_icache->c_flags = 0;
_icache->c_info = 0;
if ((flags & ~RTLD_REL_RELATIVE) &&
(shdr->sh_type == SHT_SUNW_syminfo)) {
if (syminfo(_icache, &nodirect)) {
cleanup(ielf, oelf, melf, icache, mcache,
fd, opath);
return (1);
}
}
if (shdr->sh_addr == 0) {
if ((shdr->sh_type == SHT_STRTAB) &&
((strcmp(_icache->c_name,
MSG_ORIG(MSG_SCN_SHSTR))) == 0))
_icache->c_flags = FLG_C_SHSTR;
else if (flags & RTLD_STRIP) {
_icache->c_flags = FLG_C_EXCLUDE;
continue;
}
}
if ((shdr->sh_type == M_REL_SHT_TYPE) && shdr->sh_addr)
continue;
shstr_size += strlen(_icache->c_name) + 1;
if (shdr->sh_addr && ((shdr->sh_addr + shdr->sh_size) ==
(data_phdr->p_vaddr + data_phdr->p_memsz))) {
data_cache = _icache;
if (status) {
_icache++;
_icache->c_name =
(char *)MSG_ORIG(MSG_SCN_HEAP);
_icache->c_flags = FLG_C_HEAP;
_icache->c_scn = 0;
_icache->c_shdr = 0;
_icache->c_data = 0;
_icache->c_info = 0;
shstr_size += strlen(_icache->c_name) + 1;
}
}
}
_icache = icache;
for (_icache++; _icache->c_flags != FLG_C_END; _icache++) {
if ((shdr = _icache->c_shdr) == 0)
continue;
if ((shdr->sh_type == M_REL_SHT_TYPE) && shdr->sh_addr) {
rel_entsize = shdr->sh_entsize;
if (count_reloc(icache, _icache, lmp, flags, addr,
&rel_null_no, &rel_data_no, &rel_func_no,
nodirect)) {
cleanup(ielf, oelf, melf, icache, mcache,
fd, opath);
return (1);
}
}
}
_icache = icache;
for (_icache++; _icache->c_flags != FLG_C_END; _icache++) {
if ((shdr = _icache->c_shdr) == 0)
continue;
if ((shdr->sh_type == M_REL_SHT_TYPE) && shdr->sh_addr) {
if (rel_null_no) {
_icache->c_flags = FLG_C_RELOC;
_icache->c_name =
(char *)MSG_ORIG(MSG_SCN_RELOC);
}
shstr_size += strlen(_icache->c_name) + 1;
}
}
if (!data_cache) {
eprintf(lml, ERR_WARNING, MSG_INTL(MSG_IMG_DATASEC), ipath);
status = 0;
endx = 0;
}
edata = data_phdr->p_vaddr + data_phdr->p_memsz;
if (status)
edata += status->pr_brksize;
if (endx) {
endx = (Half)elf_ndxscn(data_cache->c_scn);
if (status)
endx++;
}
if ((oehdr = elf_newehdr(oelf)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_NEWEHDR), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
oehdr->e_machine = iehdr->e_machine;
oehdr->e_flags = iehdr->e_flags;
oehdr->e_type = ET_EXEC;
oehdr->e_entry = iehdr->e_entry;
if (addr)
oehdr->e_entry += addr;
if ((ophdr = elf_newphdr(oelf, iehdr->e_phnum)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_NEWPHDR), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
for (num = 0; num != iehdr->e_phnum; num++, iphdr++, ophdr++) {
*ophdr = *iphdr;
if ((ophdr->p_type != PT_INTERP) && (ophdr->p_type != PT_NOTE))
ophdr->p_vaddr += addr;
if (data_phdr == iphdr) {
if (status)
ophdr->p_memsz = edata - ophdr->p_vaddr;
ophdr->p_filesz = ophdr->p_memsz;
}
}
if ((shstr = malloc(shstr_size)) == 0) {
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
_shstr = shstr;
*_shstr++ = '\0';
_icache = icache;
for (_icache++; _icache->c_flags != FLG_C_END; _icache++) {
if (_icache->c_flags == FLG_C_EXCLUDE)
continue;
if ((scn = elf_newscn(oelf)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_NEWSCN), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
if ((shdr = elf_getshdr(scn)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_NEWSHDR), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
if (_icache->c_flags == FLG_C_HEAP) {
shdr->sh_type = SHT_PROGBITS;
shdr->sh_flags = SHF_ALLOC | SHF_WRITE;
} else
*shdr = *_icache->c_shdr;
if ((data = elf_newdata(scn)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_NEWDATA), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
if (_icache->c_flags == FLG_C_SHSTR) {
*data = *_icache->c_data;
data->d_buf = (void *)shstr;
data->d_size = shstr_size;
_icache->c_info = shstr;
if (elf_ndxscn(scn) >= SHN_LORESERVE) {
Elf_Scn *_scn;
Shdr *shdr0;
oehdr->e_shstrndx = SHN_XINDEX;
if ((_scn = elf_getscn(oelf, 0)) == NULL) {
eprintf(lml, ERR_ELF,
MSG_ORIG(MSG_ELF_GETSCN), opath);
cleanup(ielf, oelf, melf, icache,
mcache, fd, opath);
return (1);
}
shdr0 = elf_getshdr(_scn);
shdr0->sh_link = elf_ndxscn(scn);
} else {
oehdr->e_shstrndx = (Half)elf_ndxscn(scn);
}
} else if (_icache->c_flags == FLG_C_HEAP) {
data->d_buf = status->pr_brkbase;
data->d_type = ELF_T_BYTE;
data->d_size = (size_t)status->pr_brksize;
data->d_off = 0;
data->d_align = 1;
data->d_version = EV_CURRENT;
shdr->sh_addr = data_cache->c_shdr->sh_addr +
data_cache->c_shdr->sh_size;
} else if (_icache->c_flags == FLG_C_RELOC) {
*data = *_icache->c_data;
shdr->sh_info = 0;
} else {
*data = *_icache->c_data;
if ((shdr->sh_addr) && (flags & RTLD_MEMORY))
data->d_buf = (void *)(shdr->sh_addr + addr);
if (shdr->sh_type == SHT_NOBITS) {
shdr->sh_type = SHT_PROGBITS;
if (!(flags & RTLD_MEMORY)) {
if ((data->d_buf = calloc(1,
data->d_size)) == 0) {
cleanup(ielf, oelf, melf,
icache, mcache, fd, opath);
return (1);
}
}
}
}
shdr->sh_name = (Word)(_shstr - shstr);
(void) strcpy(_shstr, _icache->c_name);
_shstr = _shstr + strlen(_icache->c_name) + 1;
if (shdr->sh_addr)
shdr->sh_addr += addr;
if (status && endx && (shdr->sh_link >= endx))
shdr->sh_link++;
}
if (elf_update(oelf, ELF_C_WRIMAGE) == -1) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_UPDATE), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
if ((melf = elf_begin(0, ELF_C_IMAGE, oelf)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_BEGIN), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
if ((mehdr = elf_getehdr(melf)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_GETEHDR), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
if (elf_getshdrnum(melf, &shndx) == -1) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_GETSHDRNUM), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
if ((mcache = calloc(shndx, sizeof (Cache))) == 0) {
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
_mcache = mcache;
_mcache++;
for (scn = 0; scn = elf_nextscn(melf, scn); _mcache++) {
if ((_mcache->c_shdr = elf_getshdr(scn)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_GETSHDR), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
if ((_mcache->c_data = elf_getdata(scn, NULL)) == NULL) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_GETDATA), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
}
_mcache = &mcache[0];
for (_icache = &icache[1]; _icache->c_flags != FLG_C_END; _icache++) {
if (_icache->c_flags == FLG_C_EXCLUDE)
continue;
_mcache++;
shdr = _mcache->c_shdr;
if ((shdr->sh_type == SHT_SYMTAB) ||
(shdr->sh_type == SHT_DYNSYM) ||
(shdr->sh_type == SHT_SUNW_LDYNSYM)) {
update_sym(mcache, _mcache, edata, endx, addr);
continue;
}
if (shdr->sh_type == M_REL_SHT_TYPE) {
if (rel_base == (Rel *)0) {
rel_base = (Rel *)_mcache->c_data->d_buf;
rel_null = rel_base;
rel_data = (Rel *)((Xword)rel_null +
(rel_null_no * rel_entsize));
rel_func = (Rel *)((Xword)rel_data +
(rel_data_no * rel_entsize));
}
update_reloc(mcache, icache, _icache, opath, lmp,
&rel_null, &rel_data, &rel_func);
continue;
}
if (shdr->sh_type == SHT_DYNAMIC)
dyn_cache = _mcache;
}
if (dyn_cache) {
Xword off = (Xword)rel_base - (Xword)mehdr;
if (!addr)
off += ADDR(lmp);
if (update_dynamic(mcache, dyn_cache, lmp, flags, addr, off,
opath, rel_null_no, rel_data_no, rel_func_no, rel_entsize,
elf_checksum(melf))) {
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
}
if (elf_update(oelf, ELF_C_WRITE) == -1) {
eprintf(lml, ERR_ELF, MSG_ORIG(MSG_ELF_UPDATE), opath);
cleanup(ielf, oelf, melf, icache, mcache, fd, opath);
return (1);
}
cleanup(ielf, oelf, melf, icache, mcache, fd, 0);
return (0);
}