#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <memory.h>
#include <sys/sysmacros.h>
#include <sys/machelf.h>
#include "Pcontrol.h"
#include "Psymtab_machelf.h"
static char shstr[] =
".shstrtab\0.dynsym\0.dynstr\0.dynamic\0.plt\0.SUNW_ldynsym";
#define SHSTR_NDX_shstrtab 0
#define SHSTR_NDX_dynsym 10
#define SHSTR_NDX_dynstr 18
#define SHSTR_NDX_dynamic 26
#define SHSTR_NDX_plt 35
#define SHSTR_NDX_SUNW_ldynsym 40
#ifdef _ELF64
#define SH_ADDRALIGN 8
#else
#define SH_ADDRALIGN 4
#endif
#ifdef __sparc
#define PLTREL_MIN_ENTRIES 4
#else
#ifdef __lint
#define PLTREL_MIN_ENTRIES 1
#else
#define PLTREL_MIN_ENTRIES 0
#endif
#endif
#ifdef _ELF64
#define fake_elf fake_elf64
#else
#define fake_elf fake_elf32
#endif
Elf *
fake_elf(struct ps_prochandle *P, file_info_t *fptr, uintptr_t addr,
Ehdr *ehdr, uint_t phnum, Phdr *phdr)
{
enum {
DI_PLTGOT,
DI_JMPREL,
DI_PLTRELSZ,
DI_PLTREL,
DI_SYMTAB,
DI_HASH,
DI_SYMENT,
DI_STRTAB,
DI_STRSZ,
DI_SUNW_SYMTAB,
DI_SUNW_SYMSZ,
DI_NENT
};
const int di_req_mask = (1 << DI_SYMTAB) |
(1 << DI_SYMENT) | (1 << DI_STRTAB) | (1 << DI_STRSZ);
int di_mask = 0;
size_t size = 0;
caddr_t elfdata = NULL;
Elf *elf;
size_t dynsym_size = 0, ldynsym_size;
int dynstr_shndx;
Ehdr *ep;
Shdr *sp;
Dyn *dp = NULL;
Dyn *d[DI_NENT] = { 0 };
uint_t i;
Off off;
size_t pltsz = 0, pltentries = 0;
uintptr_t hptr = (uintptr_t)NULL;
Word hnchains = 0, hnbuckets = 0;
if (ehdr->e_type == ET_DYN)
phdr->p_vaddr += addr;
if (P->rap != NULL) {
if (rd_get_dyns(P->rap, addr, (void **)&dp, NULL) != RD_OK)
goto bad;
} else {
if ((dp = malloc(phdr->p_filesz)) == NULL)
goto bad;
if (Pread(P, dp, phdr->p_filesz, phdr->p_vaddr) !=
phdr->p_filesz)
goto bad;
}
for (i = 0; i < phdr->p_filesz / sizeof (Dyn); i++) {
switch (dp[i].d_tag) {
case DT_PLTGOT:
d[DI_PLTGOT] = &dp[i];
break;
case DT_JMPREL:
d[DI_JMPREL] = &dp[i];
break;
case DT_PLTRELSZ:
d[DI_PLTRELSZ] = &dp[i];
break;
case DT_PLTREL:
d[DI_PLTREL] = &dp[i];
break;
case DT_SYMTAB:
d[DI_SYMTAB] = &dp[i];
di_mask |= (1 << DI_SYMTAB);
break;
case DT_HASH:
d[DI_HASH] = &dp[i];
di_mask |= (1 << DI_HASH);
break;
case DT_SYMENT:
d[DI_SYMENT] = &dp[i];
di_mask |= (1 << DI_SYMENT);
break;
case DT_SUNW_SYMTAB:
d[DI_SUNW_SYMTAB] = &dp[i];
break;
case DT_SUNW_SYMSZ:
d[DI_SUNW_SYMSZ] = &dp[i];
break;
case DT_STRTAB:
d[DI_STRTAB] = &dp[i];
di_mask |= (1 << DI_STRTAB);
break;
case DT_STRSZ:
d[DI_STRSZ] = &dp[i];
di_mask |= (1 << DI_STRSZ);
break;
}
}
if ((di_mask & di_req_mask) != di_req_mask) {
Pdprintf("text section missing required dynamic entries: "
"required 0x%x, found 0x%x\n", di_req_mask, di_mask);
goto bad;
}
if ((d[DI_SUNW_SYMTAB] != NULL) && (d[DI_SUNW_SYMSZ] != NULL) &&
((d[DI_SYMTAB]->d_un.d_ptr <= d[DI_SUNW_SYMTAB]->d_un.d_ptr) ||
(d[DI_SYMTAB]->d_un.d_ptr >= (d[DI_SUNW_SYMTAB]->d_un.d_ptr +
d[DI_SUNW_SYMSZ]->d_un.d_val)))) {
d[DI_SUNW_SYMTAB] = NULL;
d[DI_SUNW_SYMSZ] = NULL;
}
size = sizeof (Ehdr);
size += phnum * ehdr->e_phentsize;
size += sizeof (Shdr);
size += sizeof (Shdr);
size += roundup(sizeof (shstr), SH_ADDRALIGN);
if (d[DI_HASH] != NULL) {
Word hash[2];
hptr = d[DI_HASH]->d_un.d_ptr;
if (ehdr->e_type == ET_DYN)
hptr += addr;
if (Pread(P, hash, sizeof (hash), hptr) != sizeof (hash)) {
Pdprintf("Pread of .hash at %lx failed\n",
(long)(hptr));
goto bad;
}
hnbuckets = hash[0];
hnchains = hash[1];
}
if (d[DI_SUNW_SYMTAB] != NULL) {
size += sizeof (Shdr);
ldynsym_size = (size_t)d[DI_SUNW_SYMSZ]->d_un.d_val;
dynsym_size = ldynsym_size - (d[DI_SYMTAB]->d_un.d_ptr
- d[DI_SUNW_SYMTAB]->d_un.d_ptr);
ldynsym_size -= dynsym_size;
dynstr_shndx = 4;
} else {
dynsym_size = sizeof (Sym) * hnchains;
ldynsym_size = 0;
dynstr_shndx = 3;
}
size += sizeof (Shdr) + ldynsym_size + dynsym_size;
size += sizeof (Shdr);
size += roundup(d[DI_STRSZ]->d_un.d_val, SH_ADDRALIGN);
size += sizeof (Shdr);
size += roundup(phdr->p_filesz, SH_ADDRALIGN);
if (d[DI_PLTGOT] != NULL && d[DI_JMPREL] != NULL &&
d[DI_PLTRELSZ] != NULL && d[DI_PLTREL] != NULL) {
size_t pltrelsz = d[DI_PLTRELSZ]->d_un.d_val;
if (d[DI_PLTREL]->d_un.d_val == DT_RELA) {
pltentries = pltrelsz / sizeof (Rela);
} else if (d[DI_PLTREL]->d_un.d_val == DT_REL) {
pltentries = pltrelsz / sizeof (Rel);
} else {
#if ((defined(__i386) || defined(__amd64)) && !defined(_ELF64))
pltentries = pltrelsz / sizeof (Rel);
Pdprintf("DI_PLTREL not found, defaulting to Rel");
#else
pltentries = pltrelsz / sizeof (Rela);
Pdprintf("DI_PLTREL not found, defaulting to Rela");
#endif
}
if (pltentries < PLTREL_MIN_ENTRIES) {
Pdprintf("too few PLT relocation entries "
"(found %lu, expected at least %d)\n",
(long)pltentries, PLTREL_MIN_ENTRIES);
goto bad;
}
if (pltentries < PLTREL_MIN_ENTRIES + 2)
goto done_with_plt;
pltsz = (pltentries + M_PLT_XNumber) * M_PLT_ENTSIZE;
#if defined(__sparc)
pltsz += 4;
#endif
size += sizeof (Shdr);
size += roundup(pltsz, SH_ADDRALIGN);
}
done_with_plt:
if ((elfdata = calloc(1, size)) == NULL) {
Pdprintf("failed to allocate size %ld\n", (long)size);
goto bad;
}
ep = (Ehdr *)elfdata;
(void) memcpy(ep, ehdr, offsetof(Ehdr, e_phoff));
ep->e_ehsize = sizeof (Ehdr);
ep->e_phoff = sizeof (Ehdr);
ep->e_phentsize = ehdr->e_phentsize;
ep->e_phnum = phnum;
ep->e_shoff = ep->e_phoff + phnum * ep->e_phentsize;
ep->e_shentsize = sizeof (Shdr);
ep->e_shnum = 5 + (pltsz != 0) + (d[DI_SUNW_SYMTAB] != NULL);
ep->e_shstrndx = 1;
sp = (Shdr *)(elfdata + ep->e_shoff);
off = ep->e_shoff + ep->e_shentsize * ep->e_shnum;
if (Pread(P, &elfdata[ep->e_phoff], phnum * ep->e_phentsize,
addr + ehdr->e_phoff) != phnum * ep->e_phentsize) {
Pdprintf("failed to read program headers\n");
goto bad;
}
sp++;
sp->sh_name = SHSTR_NDX_shstrtab;
sp->sh_type = SHT_STRTAB;
sp->sh_flags = SHF_STRINGS;
sp->sh_addr = 0;
sp->sh_offset = off;
sp->sh_size = sizeof (shstr);
sp->sh_link = 0;
sp->sh_info = 0;
sp->sh_addralign = 1;
sp->sh_entsize = 0;
(void) memcpy(&elfdata[off], shstr, sizeof (shstr));
off += roundup(sp->sh_size, SH_ADDRALIGN);
sp++;
if (d[DI_SUNW_SYMTAB] != NULL) {
sp->sh_name = SHSTR_NDX_SUNW_ldynsym;
sp->sh_type = SHT_SUNW_LDYNSYM;
sp->sh_flags = SHF_ALLOC;
sp->sh_addr = d[DI_SUNW_SYMTAB]->d_un.d_ptr;
if (ehdr->e_type == ET_DYN)
sp->sh_addr += addr;
sp->sh_offset = off;
sp->sh_size = ldynsym_size;
sp->sh_link = dynstr_shndx;
sp->sh_info = sp->sh_size / sizeof (Sym);
sp->sh_addralign = SH_ADDRALIGN;
sp->sh_entsize = sizeof (Sym);
if (Pread(P, &elfdata[off], sp->sh_size,
sp->sh_addr) != sp->sh_size) {
Pdprintf("failed to read .SUNW_ldynsym at %lx\n",
(long)sp->sh_addr);
goto bad;
}
off += sp->sh_size;
sp++;
}
sp->sh_name = SHSTR_NDX_dynsym;
sp->sh_type = SHT_DYNSYM;
sp->sh_flags = SHF_ALLOC;
sp->sh_addr = d[DI_SYMTAB]->d_un.d_ptr;
if (ehdr->e_type == ET_DYN)
sp->sh_addr += addr;
sp->sh_offset = off;
sp->sh_size = dynsym_size;
sp->sh_link = dynstr_shndx;
sp->sh_info = 1;
sp->sh_addralign = SH_ADDRALIGN;
sp->sh_entsize = sizeof (Sym);
if (Pread(P, &elfdata[off], sp->sh_size,
sp->sh_addr) != sp->sh_size) {
Pdprintf("failed to read .dynsym at %lx\n",
(long)sp->sh_addr);
goto bad;
}
off += roundup(sp->sh_size, SH_ADDRALIGN);
sp++;
sp->sh_name = SHSTR_NDX_dynstr;
sp->sh_type = SHT_STRTAB;
sp->sh_flags = SHF_ALLOC | SHF_STRINGS;
sp->sh_addr = d[DI_STRTAB]->d_un.d_ptr;
if (ehdr->e_type == ET_DYN)
sp->sh_addr += addr;
sp->sh_offset = off;
sp->sh_size = d[DI_STRSZ]->d_un.d_val;
sp->sh_link = 0;
sp->sh_info = 0;
sp->sh_addralign = 1;
sp->sh_entsize = 0;
if (Pread(P, &elfdata[off], sp->sh_size,
sp->sh_addr) != sp->sh_size) {
Pdprintf("failed to read .dynstr\n");
goto bad;
}
off += roundup(sp->sh_size, SH_ADDRALIGN);
sp++;
sp->sh_name = SHSTR_NDX_dynamic;
sp->sh_type = SHT_DYNAMIC;
sp->sh_flags = SHF_WRITE | SHF_ALLOC;
sp->sh_addr = phdr->p_vaddr;
if (ehdr->e_type == ET_DYN)
sp->sh_addr -= addr;
sp->sh_offset = off;
sp->sh_size = phdr->p_filesz;
sp->sh_link = dynstr_shndx;
sp->sh_info = 0;
sp->sh_addralign = SH_ADDRALIGN;
sp->sh_entsize = sizeof (Dyn);
(void) memcpy(&elfdata[off], dp, sp->sh_size);
off += roundup(sp->sh_size, SH_ADDRALIGN);
sp++;
if (pltsz != 0) {
ulong_t plt_symhash;
uint_t htmp, ndx;
uintptr_t strtabptr, strtabname;
Sym sym, *symtabptr;
uint_t *hash;
char strbuf[sizeof ("_PROCEDURE_LINKAGE_TABLE_")];
strtabptr = d[DI_STRTAB]->d_un.d_ptr;
symtabptr = (Sym *)(uintptr_t)d[DI_SYMTAB]->d_un.d_ptr;
if (ehdr->e_type == ET_DYN) {
strtabptr += addr;
symtabptr = (Sym*)((uintptr_t)symtabptr + addr);
}
if ((hptr == (uintptr_t)NULL) || (hnbuckets == 0) ||
(hnchains == 0)) {
Pdprintf("empty or missing .hash\n");
goto badplt;
}
plt_symhash = elf_hash("_PROCEDURE_LINKAGE_TABLE_");
htmp = plt_symhash % hnbuckets;
hash = &((uint_t *)hptr)[2 + htmp];
if (Pread(P, &ndx, sizeof (ndx), (uintptr_t)hash) !=
sizeof (ndx)) {
Pdprintf("Pread of .hash at %lx failed\n", (long)hash);
goto badplt;
}
while (ndx) {
if (Pread(P, &sym, sizeof (sym),
(uintptr_t)&symtabptr[ndx]) != sizeof (sym)) {
Pdprintf("Pread of .symtab at %lx failed\n",
(long)&symtabptr[ndx]);
goto badplt;
}
strtabname = strtabptr + sym.st_name;
if (Pread_string(P, strbuf, sizeof (strbuf),
strtabname) < 0) {
Pdprintf("Pread of .strtab at %lx failed\n",
(long)strtabname);
goto badplt;
}
if (strcmp("_PROCEDURE_LINKAGE_TABLE_", strbuf) == 0)
break;
hash = &((uint_t *)hptr)[2 + hnbuckets + ndx];
if (Pread(P, &ndx, sizeof (ndx), (uintptr_t)hash) !=
sizeof (ndx)) {
Pdprintf("Pread of .hash at %lx failed\n",
(long)hash);
goto badplt;
}
}
#if defined(__sparc)
if (sym.st_value != d[DI_PLTGOT]->d_un.d_ptr) {
Pdprintf("warning: DI_PLTGOT (%lx) doesn't match "
".plt symbol pointer (%lx)",
(long)d[DI_PLTGOT]->d_un.d_ptr,
(long)sym.st_value);
}
#endif
if (ndx == 0) {
Pdprintf(
"Failed to find \"_PROCEDURE_LINKAGE_TABLE_\"\n");
goto badplt;
}
sp->sh_name = SHSTR_NDX_plt;
sp->sh_type = SHT_PROGBITS;
sp->sh_flags = SHF_WRITE | SHF_ALLOC | SHF_EXECINSTR;
sp->sh_addr = sym.st_value;
if (ehdr->e_type == ET_DYN)
sp->sh_addr += addr;
sp->sh_offset = off;
sp->sh_size = pltsz;
sp->sh_link = 0;
sp->sh_info = 0;
sp->sh_addralign = SH_ADDRALIGN;
sp->sh_entsize = M_PLT_ENTSIZE;
if (Pread(P, &elfdata[off], sp->sh_size, sp->sh_addr) !=
sp->sh_size) {
Pdprintf("failed to read .plt at %lx\n",
(long)sp->sh_addr);
goto badplt;
}
off += roundup(sp->sh_size, SH_ADDRALIGN);
sp++;
}
badplt:
sp++;
assert(((uintptr_t)(sp) - 1) < ((uintptr_t)elfdata + size));
free(dp);
if ((elf = elf_memory(elfdata, size)) == NULL) {
Pdprintf("failed to create ELF object "
"in memory for size %ld\n", (long)size);
free(elfdata);
return (NULL);
}
fptr->file_elfmem = elfdata;
return (elf);
bad:
if (dp != NULL)
free(dp);
if (elfdata != NULL)
free(elfdata);
return (NULL);
}