diff options
Diffstat (limited to 'src/modelops')
-rw-r--r-- | src/modelops/check.c | 54 | ||||
-rw-r--r-- | src/modelops/clone.c | 41 | ||||
-rw-r--r-- | src/modelops/dump.c | 239 | ||||
-rw-r--r-- | src/modelops/fromFile.c | 487 | ||||
-rw-r--r-- | src/modelops/layout.c | 320 | ||||
-rw-r--r-- | src/modelops/phdr.c | 36 | ||||
-rw-r--r-- | src/modelops/reladd.c | 229 | ||||
-rw-r--r-- | src/modelops/relocate.c | 169 | ||||
-rw-r--r-- | src/modelops/section.c | 193 | ||||
-rw-r--r-- | src/modelops/toFile.c | 115 |
10 files changed, 1883 insertions, 0 deletions
diff --git a/src/modelops/check.c b/src/modelops/check.c new file mode 100644 index 0000000..5234bef --- /dev/null +++ b/src/modelops/check.c @@ -0,0 +1,54 @@ +#include <assert.h> +#include <stdlib.h> +#include <sys/types.h> +#include <libelfu/libelfu.h> + + +int elfu_mCheck(ElfuElf *me) +{ + size_t numSecs; + ElfuScn **sortedSecs; + size_t i; + + sortedSecs = elfu_mScnSortedByOffset(me, &numSecs); + if (!sortedSecs) { + return -1; + } + + + /* Check for overlapping sections */ + for (i = 0; i < numSecs - 1; i++) { + if (sortedSecs[i]->shdr.sh_offset + SCNFILESIZE(&sortedSecs[i]->shdr) + > sortedSecs[i+1]->shdr.sh_offset) { + ELFU_WARN("elfu_check: Found overlapping sections: %s and %s.\n", + elfu_mScnName(me, sortedSecs[i]), + elfu_mScnName(me, sortedSecs[i+1])); + } + } + + + /* Check for sections overlapping with EHDR */ + for (i = 0; i < numSecs; i++) { + if (sortedSecs[i]->shdr.sh_offset < me->ehdr.e_ehsize) { + ELFU_WARN("elfu_check: Found section overlapping with EHDR: %s.\n", + elfu_mScnName(me, sortedSecs[i])); + } + } + + + /* Check for sections overlapping with PHDRs */ + for (i = 0; i < numSecs; i++) { + if (OVERLAPPING(sortedSecs[i]->shdr.sh_offset, + SCNFILESIZE(&sortedSecs[i]->shdr), + me->ehdr.e_phoff, + me->ehdr.e_phentsize * me->ehdr.e_phnum)) { + ELFU_WARN("elfu_check: Found section overlapping with PHDRs: %s.\n", + elfu_mScnName(me, sortedSecs[i])); + } + } + + + free(sortedSecs); + + return 0; +} diff --git a/src/modelops/clone.c b/src/modelops/clone.c new file mode 100644 index 0000000..ca4b38f --- /dev/null +++ b/src/modelops/clone.c @@ -0,0 +1,41 @@ +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <libelfu/libelfu.h> + +ElfuScn* elfu_mCloneScn(ElfuScn *ms) +{ + ElfuScn *newscn; + + assert(ms); + + newscn = malloc(sizeof(ElfuScn)); + if (!newscn) { + ELFU_WARN("elfu_nCloneScn: Could not allocate memory for new ElfuScn.\n"); + return NULL; + } + + newscn->shdr = ms->shdr; + newscn->data = ms->data; + if (ms->data.d_buf) { + void *newbuf = malloc(ms->data.d_size); + if (!newbuf) { + ELFU_WARN("elfu_nCloneScn: Could not allocate memory for new data buffer.\n"); + free(newscn); + return NULL; + } + + memcpy(newbuf, ms->data.d_buf, ms->data.d_size); + newscn->data.d_buf = newbuf; + } + + newscn->linkptr = NULL; + newscn->infoptr = NULL; + + newscn->oldptr = ms; + + ms->symtab = NULL; + ms->reltab = NULL; + + return newscn; +} diff --git a/src/modelops/dump.c b/src/modelops/dump.c new file mode 100644 index 0000000..27556f9 --- /dev/null +++ b/src/modelops/dump.c @@ -0,0 +1,239 @@ +#include <assert.h> +#include <libelfu/libelfu.h> + + +static char* segmentTypeStr(Elf32_Word p_type) +{ + switch(p_type) { + case PT_NULL: /* 0 */ + return "NULL"; + case PT_LOAD: /* 1 */ + return "LOAD"; + case PT_DYNAMIC: /* 2 */ + return "DYNAMIC"; + case PT_INTERP: /* 3 */ + return "INTERP"; + case PT_NOTE: /* 4 */ + return "NOTE"; + case PT_SHLIB: /* 5 */ + return "SHLIB"; + case PT_PHDR: /* 6 */ + return "PHDR"; + case PT_TLS: /* 7 */ + return "TLS"; + case PT_NUM: /* 8 */ + return "NUM"; + case PT_GNU_EH_FRAME: /* 0x6474e550 */ + return "GNU_EH_FRAME"; + case PT_GNU_STACK: /* 0x6474e551 */ + return "GNU_STACK"; + case PT_GNU_RELRO: /* 0x6474e552 */ + return "GNU_RELRO"; + } + + return "-?-"; +} + +static char* sectionTypeStr(Elf32_Word sh_type) +{ + switch(sh_type) { + case SHT_NULL: /* 0 */ + return "NULL"; + case SHT_PROGBITS: /* 1 */ + return "PROGBITS"; + case SHT_SYMTAB: /* 2 */ + return "SYMTAB"; + case SHT_STRTAB: /* 3 */ + return "STRTAB"; + case SHT_RELA: /* 4 */ + return "RELA"; + case SHT_HASH: /* 5 */ + return "HASH"; + case SHT_DYNAMIC: /* 6 */ + return "DYNAMIC"; + case SHT_NOTE: /* 7 */ + return "NOTE"; + case SHT_NOBITS: /* 8 */ + return "NOBITS"; + case SHT_REL: /* 9 */ + return "REL"; + case SHT_SHLIB: /* 10 */ + return "SHLIB"; + case SHT_DYNSYM: /* 11 */ + return "DYNSYM"; + case SHT_INIT_ARRAY: /* 14 */ + return "INIT_ARRAY"; + case SHT_FINI_ARRAY: /* 15 */ + return "FINI_ARRAY"; + case SHT_PREINIT_ARRAY: /* 16 */ + return "PREINIT_ARRAY"; + case SHT_GROUP: /* 17 */ + return "SHT_GROUP"; + case SHT_SYMTAB_SHNDX: /* 18 */ + return "SYMTAB_SHNDX"; + case SHT_NUM: /* 19 */ + return "NUM"; + + case SHT_GNU_ATTRIBUTES: /* 0x6ffffff5 */ + return "GNU_ATTRIBUTES"; + case SHT_GNU_HASH: /* 0x6ffffff6 */ + return "GNU_HASH"; + case SHT_GNU_LIBLIST: /* 0x6ffffff7 */ + return "GNU_LIBLIST"; + case SHT_GNU_verdef: /* 0x6ffffffd */ + return "GNU_verdef"; + case SHT_GNU_verneed: /* 0x6ffffffe */ + return "GNU_verneed"; + case SHT_GNU_versym: /* 0x6fffffff */ + return "GNU_versym"; + } + + return "-?-"; +} + + + + +void elfu_mDumpPhdr(ElfuElf *me, ElfuPhdr *mp) +{ + assert(me); + assert(mp); + + ELFU_INFO("%12s %8jx %8jx %8jx %8jx %8jx %8jx %8jx %8jx\n", + segmentTypeStr(mp->phdr.p_type), + (uintmax_t) mp->phdr.p_type, + (uintmax_t) mp->phdr.p_offset, + (uintmax_t) mp->phdr.p_vaddr, + (uintmax_t) mp->phdr.p_paddr, + (uintmax_t) mp->phdr.p_filesz, + (uintmax_t) mp->phdr.p_memsz, + (uintmax_t) mp->phdr.p_flags, + (uintmax_t) mp->phdr.p_align); + + if (!CIRCLEQ_EMPTY(&mp->childPhdrList)) { + ElfuPhdr *mpc; + + ELFU_INFO(" -> Child PHDRs:\n"); + CIRCLEQ_FOREACH(mpc, &mp->childPhdrList, elemChildPhdr) { + ELFU_INFO(" * %-8s @ %8jx\n", + segmentTypeStr(mpc->phdr.p_type), + mpc->phdr.p_vaddr); + } + } + + if (!CIRCLEQ_EMPTY(&mp->childScnList)) { + ElfuScn *msc; + + ELFU_INFO(" -> Child sections:\n"); + CIRCLEQ_FOREACH(msc, &mp->childScnList, elemChildScn) { + ELFU_INFO(" * %-17s @ %8jx\n", + elfu_mScnName(me, msc), + msc->shdr.sh_addr); + } + } +} + + +void elfu_mDumpScn(ElfuElf *me, ElfuScn *ms) +{ + char *namestr, *typestr, *linkstr, *infostr; + + assert(me); + assert(ms); + + namestr = elfu_mScnName(me, ms); + typestr = sectionTypeStr(ms->shdr.sh_type); + linkstr = ms->linkptr ? elfu_mScnName(me, ms->linkptr) : ""; + infostr = ms->infoptr ? elfu_mScnName(me, ms->infoptr) : ""; + + ELFU_INFO("%-17s %-15s %8jx %9jx %8jx %2jx %2jx %2jd %-17s %-17s\n", + namestr, + typestr, + ms->shdr.sh_addr, + ms->shdr.sh_offset, + ms->shdr.sh_size, + ms->shdr.sh_entsize, + ms->shdr.sh_flags, + ms->shdr.sh_addralign, + linkstr, + infostr); +} + + +void elfu_mDumpEhdr(ElfuElf *me) +{ + assert(me); + + ELFU_INFO("ELF header:\n"); + ELFU_INFO(" %d-bit ELF object\n", me->elfclass == ELFCLASS32 ? 32 : 64); + + ELFU_INFO(" EHDR:\n"); + + ELFU_INFO(" e_type %8x\n", me->ehdr.e_type); + ELFU_INFO(" e_machine %8x\n", me->ehdr.e_machine); + ELFU_INFO(" e_version %8x\n", me->ehdr.e_version); + ELFU_INFO(" e_entry %8jx\n", me->ehdr.e_entry); + ELFU_INFO(" e_phoff %8jx\n", me->ehdr.e_phoff); + ELFU_INFO(" e_shoff %8jx\n", me->ehdr.e_shoff); + ELFU_INFO(" e_flags %8x\n", me->ehdr.e_flags); + ELFU_INFO(" e_ehsize %8x\n", me->ehdr.e_ehsize); + ELFU_INFO(" e_phentsize %8x\n", me->ehdr.e_phentsize); + ELFU_INFO(" e_shentsize %8x\n", me->ehdr.e_shentsize); + + ELFU_INFO(" shstrtab: %s\n", me->shstrtab ? elfu_mScnName(me, me->shstrtab) : "(none)"); +} + + + +static void* subScnDump(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2) +{ + (void) aux1; + (void) aux2; + + printf(" [%4d] ", elfu_mScnIndex(me, ms)); + elfu_mDumpScn(me, ms); + + return NULL; +} + + +void elfu_mDumpElf(ElfuElf *me) +{ + ElfuPhdr *mp; + ElfuScn *ms; + size_t i; + + assert(me); + + + elfu_mDumpEhdr(me); + ELFU_INFO("\n"); + + + ELFU_INFO("Segments:\n"); + ELFU_INFO(" # (type) p_type p_offset p_vaddr p_paddr p_filesz p_memsz p_flags p_align\n"); + ELFU_INFO(" | | | | | | | | | \n"); + i = 0; + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + printf(" [%4d] ", i); + elfu_mDumpPhdr(me, mp); + i++; + } + ELFU_INFO("\n"); + + + ELFU_INFO("Orphaned sections:\n"); + CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) { + ELFU_INFO(" * %-17s @ %8jx\n", + elfu_mScnName(me, ms), + ms->shdr.sh_addr); + } + ELFU_INFO("\n"); + + + ELFU_INFO("Sections:\n"); + ELFU_INFO(" # Name sh_type sh_addr sh_offset sh_size ES Fl Al sh_link sh_info \n"); + ELFU_INFO(" | | | | | | | | | | \n"); + elfu_mScnForall(me, subScnDump, &i, NULL); + ELFU_INFO("\n"); +} diff --git a/src/modelops/fromFile.c b/src/modelops/fromFile.c new file mode 100644 index 0000000..23105e6 --- /dev/null +++ b/src/modelops/fromFile.c @@ -0,0 +1,487 @@ +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <libelfu/libelfu.h> + + +static char* symstr(ElfuScn *symtab, size_t off) +{ + assert(symtab); + assert(symtab->linkptr); + assert(symtab->linkptr->data.d_buf); + assert(off < symtab->linkptr->data.d_size); + + return &(((char*)symtab->linkptr->data.d_buf)[off]); +} + + +static ElfuSymtab* symtabFromScn32(ElfuScn *ms, ElfuScn**origScnArr) +{ + ElfuSymtab *st; + size_t i; + + assert(ms); + assert(ms->data.d_buf); + assert(origScnArr); + + + st = malloc(sizeof(*st)); + if (!st) { + ELFU_WARN("elfu_mSymtabFromScn32: malloc() failed for st.\n"); + goto ERROR; + } + + CIRCLEQ_INIT(&st->syms); + + + for (i = 1; (i + 1) * sizeof(Elf32_Sym) <= ms->shdr.sh_size; i++) { + Elf32_Sym *cursym = &(((Elf32_Sym*)ms->data.d_buf)[i]); + ElfuSym *sym = malloc(sizeof(*sym)); + assert(sym); + + sym->name = symstr(ms, cursym->st_name); + sym->value = cursym->st_value; + sym->size = cursym->st_size; + sym->bind = ELF32_ST_BIND(cursym->st_info); + sym->type = ELF32_ST_TYPE(cursym->st_info); + sym->other = cursym->st_other; + + switch (cursym->st_shndx) { + case SHN_UNDEF: + case SHN_ABS: + case SHN_COMMON: + sym->scnptr = NULL; + sym->shndx = cursym->st_shndx; + break; + default: + sym->scnptr = origScnArr[cursym->st_shndx - 1]; + break; + } + + + CIRCLEQ_INSERT_TAIL(&st->syms, sym, elem); + } + + + return st; + + ERROR: + if (st) { + free(st); + } + return NULL; +} + + +static ElfuReltab* reltabFromScn32(ElfuScn *ms) +{ + ElfuReltab *rt; + size_t i; + + assert(ms); + assert(ms->data.d_buf); + + + rt = malloc(sizeof(*rt)); + if (!rt) { + ELFU_WARN("elfu_mReltabFromScn32: malloc() failed for rt.\n"); + goto ERROR; + } + + CIRCLEQ_INIT(&rt->rels); + + + for (i = 0; (i + 1) * sizeof(Elf32_Rel) <= ms->shdr.sh_size; i++) { + Elf32_Rel *currel = &(((Elf32_Rel*)ms->data.d_buf)[i]); + ElfuRel *rel; + + rel = malloc(sizeof(*rel)); + assert(rel); + + rel->offset = currel->r_offset; + + rel->sym = ELF32_R_SYM(currel->r_info); + rel->type = ELF32_R_TYPE(currel->r_info); + + rel->addendUsed = 0; + rel->addend = 0; + + CIRCLEQ_INSERT_TAIL(&rt->rels, rel, elem); + } + + + return rt; + + ERROR: + if (rt) { + free(rt); + } + return NULL; +} + + +static int cmpScnOffs(const void *ms1, const void *ms2) +{ + assert(ms1); + assert(ms2); + + ElfuScn *s1 = *(ElfuScn**)ms1; + ElfuScn *s2 = *(ElfuScn**)ms2; + + assert(s1); + assert(s2); + + + if (s1->shdr.sh_offset < s2->shdr.sh_offset) { + return -1; + } else if (s1->shdr.sh_offset == s2->shdr.sh_offset) { + return 0; + } else /* if (s1->shdr.sh_offset > s2->shdr.sh_offset) */ { + return 1; + } +} + + + +static ElfuPhdr* parentPhdr(ElfuElf *me, ElfuScn *ms) +{ + ElfuPhdr *mp; + + assert(me); + assert(ms); + + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + if (mp->phdr.p_type != PT_LOAD) { + continue; + } + + if (PHDR_CONTAINS_SCN_IN_MEMORY(&mp->phdr, &ms->shdr)) { + return mp; + } + + /* Give sections a second chance if they do not have any sh_addr + * at all. */ + /* Actually we don't, because it's ambiguous. + * Re-enable for experiments with strangely-formatted files. + if (ms->shdr.sh_addr == 0 + && PHDR_CONTAINS_SCN_IN_FILE(&mp->phdr, &ms->shdr) + && OFFS_END(ms->shdr.sh_offset, ms->shdr.sh_size) + <= OFFS_END(mp->phdr.p_offset, mp->phdr.p_memsz)) { + return mp; + } + */ + } + + return NULL; +} + + +static ElfuPhdr* modelFromPhdr(GElf_Phdr *phdr) +{ + ElfuPhdr *mp; + + assert(phdr); + + mp = malloc(sizeof(ElfuPhdr)); + if (!mp) { + ELFU_WARN("modelFromPhdr: malloc() failed for ElfuPhdr.\n"); + return NULL; + } + + mp->phdr = *phdr; + + CIRCLEQ_INIT(&mp->childScnList); + CIRCLEQ_INIT(&mp->childPhdrList); + + return mp; +} + + +static ElfuScn* modelFromSection(Elf_Scn *scn) +{ + ElfuScn *ms; + + assert(scn); + + ms = malloc(sizeof(ElfuScn)); + if (!ms) { + ELFU_WARN("modelFromSection: malloc() failed for ElfuScn.\n"); + goto ERROR; + } + + + assert(gelf_getshdr(scn, &ms->shdr) == &ms->shdr); + + + /* Copy each data part in source segment */ + ms->data.d_align = 1; + ms->data.d_buf = NULL; + ms->data.d_off = 0; + ms->data.d_type = ELF_T_BYTE; + ms->data.d_size = ms->shdr.sh_size; + ms->data.d_version = elf_version(EV_NONE); + if (ms->shdr.sh_type != SHT_NOBITS + && ms->shdr.sh_size > 0) { + Elf_Data *data; + + ms->data.d_buf = malloc(ms->shdr.sh_size); + if (!ms->data.d_buf) { + ELFU_WARN("modelFromSection: malloc() failed for data buffer (%jx bytes).\n", ms->shdr.sh_size); + goto ERROR; + } + + /* A non-empty section should contain at least one data block. */ + data = elf_rawdata(scn, NULL); + assert(data); + + ms->data.d_align = data->d_align; + ms->data.d_type = data->d_type; + ms->data.d_version = data->d_version; + + while (data) { + if (data->d_off + data->d_size > ms->shdr.sh_size) { + ELFU_WARN("modelFromSection: libelf delivered a bogus data blob. Skipping\n"); + } else { + memcpy(ms->data.d_buf + data->d_off, data->d_buf, data->d_size); + } + + data = elf_rawdata(scn, data); + } + } + + ms->linkptr = NULL; + ms->infoptr = NULL; + + ms->oldptr = NULL; + + ms->symtab = NULL; + ms->reltab = NULL; + + + return ms; + + ERROR: + if (ms) { + free(ms); + } + return NULL; +} + + + + +ElfuElf* elfu_mFromElf(Elf *e) +{ + ElfuElf *me; + size_t shstrndx; + size_t i, numPhdr, numShdr; + ElfuScn **secArray = NULL; + + assert(e); + if (elfu_eCheck(e)) { + goto ERROR; + } + + me = malloc(sizeof(ElfuElf)); + if (!me) { + ELFU_WARN("elfu_mFromElf: malloc() failed for ElfuElf.\n"); + goto ERROR; + } + + + /* General stuff */ + CIRCLEQ_INIT(&me->phdrList); + CIRCLEQ_INIT(&me->orphanScnList); + me->shstrtab = NULL; + + me->elfclass = gelf_getclass(e); + assert(me->elfclass != ELFCLASSNONE); + assert(gelf_getehdr(e, &me->ehdr) == &me->ehdr); + + + /* Get the section string table index */ + if (elf_getshdrstrndx(e, &shstrndx) != 0) { + shstrndx = 0; + } + + + /* Load segments */ + assert(!elf_getphdrnum(e, &numPhdr)); + for (i = 0; i < numPhdr; i++) { + GElf_Phdr phdr; + ElfuPhdr *mp; + + assert(gelf_getphdr(e, i, &phdr) == &phdr); + + mp = modelFromPhdr(&phdr); + if (!mp) { + goto ERROR; + } + + CIRCLEQ_INSERT_TAIL(&me->phdrList, mp, elem); + } + + if (numPhdr > 0) { + ElfuPhdr *mp; + + /* Find PHDR -> PHDR dependencies (needs sorted sections) */ + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + ElfuPhdr *mp2; + + if (mp->phdr.p_type != PT_LOAD) { + continue; + } + + CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) { + if (mp2 == mp) { + continue; + } + + if (mp->phdr.p_vaddr <= mp2->phdr.p_vaddr + && OFFS_END(mp2->phdr.p_vaddr, mp2->phdr.p_memsz) <= OFFS_END(mp->phdr.p_vaddr, mp->phdr.p_memsz)) { + CIRCLEQ_INSERT_TAIL(&mp->childPhdrList, mp2, elemChildPhdr); + } + } + } + } + + + /* Load sections */ + assert(!elf_getshdrnum(e, &numShdr)); + if (numShdr > 1) { + secArray = malloc((numShdr - 1) * sizeof(*secArray)); + if (!secArray) { + ELFU_WARN("elfu_mFromElf: malloc() failed for secArray.\n"); + goto ERROR; + } + + for (i = 1; i < numShdr; i++) { + Elf_Scn *scn; + ElfuScn *ms; + + scn = elf_getscn(e, i); + assert(scn); + + ms = modelFromSection(scn); + if (!ms) { + goto ERROR; + } + + secArray[i-1] = ms; + + if (i == shstrndx) { + me->shstrtab = ms; + } + } + + + /* Find sh_link and sh_info dependencies (needs sections in original order) */ + for (i = 0; i < numShdr - 1; i++) { + ElfuScn *ms = secArray[i]; + + switch (ms->shdr.sh_type) { + case SHT_REL: + case SHT_RELA: + if (ms->shdr.sh_info > 0) { + ms->infoptr = secArray[ms->shdr.sh_info - 1]; + } + case SHT_DYNAMIC: + case SHT_HASH: + case SHT_SYMTAB: + case SHT_DYNSYM: + case SHT_GNU_versym: + case SHT_GNU_verdef: + case SHT_GNU_verneed: + if (ms->shdr.sh_link > 0) { + ms->linkptr = secArray[ms->shdr.sh_link - 1]; + } + } + } + + + /* Parse symtabs (needs sections in original order) */ + for (i = 0; i < numShdr - 1; i++) { + ElfuScn *ms = secArray[i]; + + switch (ms->shdr.sh_type) { + case SHT_SYMTAB: + case SHT_DYNSYM: + if (me->elfclass == ELFCLASS32) { + ms->symtab = symtabFromScn32(ms, secArray); + } else if (me->elfclass == ELFCLASS64) { + // TODO + } + assert(ms->symtab); + break; + } + } + + + /* Parse relocations */ + for (i = 0; i < numShdr - 1; i++) { + ElfuScn *ms = secArray[i]; + + switch (ms->shdr.sh_type) { + case SHT_REL: + if (me->elfclass == ELFCLASS32) { + ms->reltab = reltabFromScn32(ms); + } else if (me->elfclass == ELFCLASS64) { + // TODO + } + assert(ms->reltab); + break; + case SHT_RELA: + if (me->elfclass == ELFCLASS32) { + // TODO + } else if (me->elfclass == ELFCLASS64) { + // TODO + } + assert(ms->reltab); + break; + } + } + + + /* Sort sections by file offset */ + qsort(secArray, numShdr - 1, sizeof(*secArray), cmpScnOffs); + + + /* Find PHDR -> Section dependencies (needs sorted sections) */ + for (i = 0; i < numShdr - 1; i++) { + ElfuScn *ms = secArray[i]; + + ElfuPhdr *parent = parentPhdr(me, ms); + + if (parent) { + GElf_Off shaddr = parent->phdr.p_vaddr + + (ms->shdr.sh_offset - parent->phdr.p_offset); + + if (ms->shdr.sh_addr == 0) { + ms->shdr.sh_addr = shaddr; + } else { + assert(ms->shdr.sh_addr == shaddr); + } + + CIRCLEQ_INSERT_TAIL(&parent->childScnList, ms, elemChildScn); + } else { + CIRCLEQ_INSERT_TAIL(&me->orphanScnList, ms, elemChildScn); + } + } + } + + + return me; + + + ERROR: + if (secArray) { + free(secArray); + } + if (me) { + // TODO: Free data structures + } + + ELFU_WARN("elfu_mFromElf: Failed to load file.\n"); + return NULL; +} diff --git a/src/modelops/layout.c b/src/modelops/layout.c new file mode 100644 index 0000000..e4b3fb1 --- /dev/null +++ b/src/modelops/layout.c @@ -0,0 +1,320 @@ +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <libelfu/libelfu.h> + + + +static GElf_Word shiftStuffAtAfterOffset(ElfuElf *me, + GElf_Off offset, + GElf_Word size) +{ + ElfuPhdr *mp; + ElfuScn *ms; + /* Force a minimum alignment, just to be sure. */ + GElf_Word align = 64; + + /* Find maximum alignment size by which we have to shift. + * Assumes alignment sizes are always 2^x. */ + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + if (mp->phdr.p_offset >= offset) { + if (mp->phdr.p_align > align) { + align = mp->phdr.p_align; + } + } + } + + CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) { + if (ms->shdr.sh_offset >= offset) { + if (ms->shdr.sh_addralign > align) { + align = ms->shdr.sh_addralign; + } + } + } + + size = ROUNDUP(size, align); + + /* Shift stuff */ + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + if (mp->phdr.p_type != PT_LOAD) { + continue; + } + + if (mp->phdr.p_offset >= offset) { + mp->phdr.p_offset += size; + + elfu_mPhdrUpdateChildOffsets(mp); + } + } + + CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) { + if (ms->shdr.sh_offset >= offset) { + ms->shdr.sh_offset += size; + } + } + + if (me->ehdr.e_phoff >= offset) { + me->ehdr.e_phoff += size; + } + + if (me->ehdr.e_shoff >= offset) { + me->ehdr.e_shoff += size; + } + + return size; +} + + + + + +/* Finds a suitable PHDR to insert a hole into and expands it + * if necessary. + * Returns memory address the hole will be mapped to, or 0 if + * the operation failed. */ +GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size, + GElf_Word align, int w, int x, + ElfuPhdr **injPhdr) +{ + ElfuPhdr *first = NULL; + ElfuPhdr *last = NULL; + ElfuPhdr *mp; + + assert(!(w && x)); + + /* Treat read-only data as executable. + * That's what the GNU toolchain does on x86. */ + if (!w && !x) { + x = 1; + } + + /* Find first and last LOAD PHDRs. + * Don't compare p_memsz - segments don't overlap in memory. */ + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + if (mp->phdr.p_type != PT_LOAD) { + continue; + } + if (!first || mp->phdr.p_vaddr < first->phdr.p_vaddr) { + first = mp; + } + if (!last || mp->phdr.p_vaddr > last->phdr.p_vaddr) { + last = mp; + } + } + + if ((w && (last->phdr.p_flags & PF_W)) + || (x && (last->phdr.p_flags & PF_X))) { + /* Need to append. */ + GElf_Off injOffset = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz); + GElf_Word injSpace = 0; + GElf_Word nobitsize = last->phdr.p_memsz - last->phdr.p_filesz; + + /* Expand NOBITS if any */ + if (nobitsize > 0) { + GElf_Off endOff = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz); + GElf_Off endAddr = OFFS_END(last->phdr.p_vaddr, last->phdr.p_filesz); + ElfuScn *ms; + + ELFU_INFO("Expanding NOBITS at address 0x%jx...\n", endAddr); + + CIRCLEQ_FOREACH(ms, &last->childScnList, elemChildScn) { + if (ms->shdr.sh_offset == endOff) { + assert(ms->shdr.sh_type == SHT_NOBITS); + assert(ms->shdr.sh_size == nobitsize); + ms->data.d_buf = malloc(ms->shdr.sh_size); + memset(ms->data.d_buf, '\0', ms->shdr.sh_size); + if (!ms->data.d_buf) { + ELFU_WARN("mExpandNobits: Could not allocate %jd bytes for NOBITS expansion. Data may be inconsistent.\n", ms->shdr.sh_size); + assert(0); + goto ERROR; + } + + ms->data.d_align = 1; + ms->data.d_off = 0; + ms->data.d_type = ELF_T_BYTE; + ms->data.d_size = ms->shdr.sh_size; + ms->data.d_version = elf_version(EV_NONE); + + ms->shdr.sh_type = SHT_PROGBITS; + ms->shdr.sh_addr = endAddr; + } + } + + injSpace += shiftStuffAtAfterOffset(me, endOff, nobitsize); + injSpace -= nobitsize; + injOffset += nobitsize; + last->phdr.p_filesz += nobitsize; + assert(last->phdr.p_filesz == last->phdr.p_memsz); + } + + /* Calculate how much space we need, taking alignment into account */ + size += ROUNDUP(injOffset, align) - injOffset; + + /* If there is not enough space left, create even more. */ + if (injSpace < size) { + injSpace += shiftStuffAtAfterOffset(me, injOffset, size - injSpace); + } + assert(injSpace >= size); + + /* Remap ourselves */ + last->phdr.p_filesz += size; + last->phdr.p_memsz += size; + + injOffset = ROUNDUP(injOffset, align); + + if (injPhdr) { + *injPhdr = last; + } + return last->phdr.p_vaddr + (injOffset - last->phdr.p_offset); + } else if ((w && (first->phdr.p_flags & PF_W)) + || (x && (first->phdr.p_flags & PF_X))) { + /* Need to prepend or split up the PHDR. */ + GElf_Off injOffset = OFFS_END(first->phdr.p_offset, first->phdr.p_filesz); + ElfuScn *ms; + + /* Round up size to take PHDR alignment into account. + * We assume that this is a multiple of the alignment asked for. */ + assert(first->phdr.p_align >= align); + size = ROUNDUP(size, first->phdr.p_align); + + /* Find first section. We assume there is at least one. */ + assert(!CIRCLEQ_EMPTY(&first->childScnList)); + injOffset = CIRCLEQ_FIRST(&first->childScnList)->shdr.sh_offset; + + /* Move our sections */ + CIRCLEQ_FOREACH(ms, &first->childScnList, elemChildScn) { + if (ms->shdr.sh_offset >= injOffset) { + ms->shdr.sh_offset += size; + } + } + + /* Move our PHDRs */ + CIRCLEQ_FOREACH(mp, &first->childPhdrList, elemChildPhdr) { + if (mp->phdr.p_offset >= injOffset) { + mp->phdr.p_offset += size; + } else { + mp->phdr.p_vaddr -= size; + mp->phdr.p_paddr -= size; + } + } + + /* Move other PHDRs and sections */ + assert(size <= shiftStuffAtAfterOffset(me, injOffset, size)); + + /* Remap ourselves */ + first->phdr.p_vaddr -= size; + first->phdr.p_paddr -= size; + first->phdr.p_filesz += size; + first->phdr.p_memsz += size; + + injOffset = ROUNDUP(injOffset, align); + + if (injPhdr) { + *injPhdr = first; + } + return first->phdr.p_vaddr + (injOffset - first->phdr.p_offset); + } + + ERROR: + if (injPhdr) { + *injPhdr = NULL; + } + return 0; +} + + + + +static int cmpPhdrOffs(const void *mp1, const void *mp2) +{ + assert(mp1); + assert(mp2); + + ElfuPhdr *p1 = *(ElfuPhdr**)mp1; + ElfuPhdr *p2 = *(ElfuPhdr**)mp2; + + assert(p1); + assert(p2); + + + if (p1->phdr.p_offset < p2->phdr.p_offset) { + return -1; + } else if (p1->phdr.p_offset == p2->phdr.p_offset) { + return 0; + } else /* if (p1->phdr.p_offset > p2->phdr.p_offset) */ { + return 1; + } +} + +int elfu_mLayoutAuto(ElfuElf *me) +{ + ElfuPhdr *mp; + ElfuScn *ms; + ElfuPhdr **phdrArr; + GElf_Off lastend = 0; + size_t i, j; + + assert(me); + + phdrArr = malloc(elfu_mPhdrCount(me) * sizeof(*phdrArr)); + if (!phdrArr) { + ELFU_WARN("elfu_mLayoutAuto: malloc failed for phdrArr.\n"); + return 1; + } + + i = 0; + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + if (mp->phdr.p_type != PT_LOAD) { + continue; + } + + phdrArr[i] = mp; + i++; + } + + /* Assume we have at least one LOAD PHDR, + * and that it ends after EHDR and PHDRs */ + assert(i > 1); + + /* Sort array by file offset */ + qsort(phdrArr, i, sizeof(*phdrArr), cmpPhdrOffs); + + lastend = OFFS_END(phdrArr[0]->phdr.p_offset, phdrArr[0]->phdr.p_filesz); + + /* Wiggle offsets of 2nd, 3rd etc so take minimum space */ + for (j = 1; j < i; j++) { + GElf_Off subalign = phdrArr[j]->phdr.p_offset % phdrArr[j]->phdr.p_align; + + if ((lastend % phdrArr[j]->phdr.p_align) <= subalign) { + lastend += subalign - (lastend % phdrArr[j]->phdr.p_align); + } else { + lastend += phdrArr[j]->phdr.p_align - ((lastend % phdrArr[j]->phdr.p_align) - subalign); + } + + phdrArr[j]->phdr.p_offset = lastend; + + elfu_mPhdrUpdateChildOffsets(phdrArr[j]); + + lastend = OFFS_END(phdrArr[j]->phdr.p_offset, phdrArr[j]->phdr.p_filesz); + } + + free(phdrArr); + + + /* Place orphaned sections afterwards, maintaining alignment */ + CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) { + lastend = ROUNDUP(lastend, ms->shdr.sh_addralign); + + ms->shdr.sh_offset = lastend; + + lastend = OFFS_END(ms->shdr.sh_offset, SCNFILESIZE(&ms->shdr)); + } + + + /* Move SHDRs to end */ + lastend = ROUNDUP(lastend, 8); + me->ehdr.e_shoff = lastend; + + + return 0; +} diff --git a/src/modelops/phdr.c b/src/modelops/phdr.c new file mode 100644 index 0000000..d26eb77 --- /dev/null +++ b/src/modelops/phdr.c @@ -0,0 +1,36 @@ +#include <assert.h> +#include <libelfu/libelfu.h> + + +size_t elfu_mPhdrCount(ElfuElf *me) +{ + ElfuPhdr *mp; + size_t i = 0; + + assert(me); + + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + i++; + } + + return i; +} + + + +void elfu_mPhdrUpdateChildOffsets(ElfuPhdr *mp) +{ + ElfuScn *ms; + ElfuPhdr *mpc; + + assert(mp); + assert(mp->phdr.p_type == PT_LOAD); + + CIRCLEQ_FOREACH(mpc, &mp->childPhdrList, elemChildPhdr) { + mpc->phdr.p_offset = mp->phdr.p_offset + (mpc->phdr.p_vaddr - mp->phdr.p_vaddr); + } + + CIRCLEQ_FOREACH(ms, &mp->childScnList, elemChildScn) { + ms->shdr.sh_offset = mp->phdr.p_offset + (ms->shdr.sh_addr - mp->phdr.p_vaddr); + } +} diff --git a/src/modelops/reladd.c b/src/modelops/reladd.c new file mode 100644 index 0000000..2fdfb48 --- /dev/null +++ b/src/modelops/reladd.c @@ -0,0 +1,229 @@ +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <libelfu/libelfu.h> + + +static int appendData(ElfuScn *ms, void *buf, size_t len) +{ + void *newbuf; + + assert(ms); + assert(ms->shdr.sh_type != SHT_NOBITS); + assert(ms->data.d_buf); + + newbuf = realloc(ms->data.d_buf, ms->shdr.sh_size + len); + if (!newbuf) { + ELFU_WARN("appendData: malloc() failed for newbuf.\n"); + return 1; + } + + ms->data.d_buf = newbuf; + memcpy(newbuf + ms->shdr.sh_size, buf, len); + ms->shdr.sh_size += len; + ms->data.d_size += len; + assert(ms->shdr.sh_size == ms->data.d_size); + + return 0; +} + + +static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *oldscn) +{ + ElfuScn *newscn = NULL; + GElf_Addr injAddr; + GElf_Off injOffset; + ElfuPhdr *injPhdr; + + if (oldscn->shdr.sh_flags & SHF_ALLOC) { + newscn = elfu_mCloneScn(oldscn); + if (!newscn) { + return NULL; + } + + if (newscn->shdr.sh_type == SHT_NOBITS) { + /* Expand this to SHT_PROGBITS, then insert as such. */ + + assert(!newscn->data.d_buf); + + newscn->data.d_buf = malloc(newscn->shdr.sh_size); + if (!newscn->data.d_buf) { + goto ERROR; + } + newscn->data.d_size = newscn->shdr.sh_size; + newscn->shdr.sh_type = SHT_PROGBITS; + } + + injAddr = elfu_mLayoutGetSpaceInPhdr(me, + newscn->shdr.sh_size, + newscn->shdr.sh_addralign, + newscn->shdr.sh_flags & SHF_WRITE, + newscn->shdr.sh_flags & SHF_EXECINSTR, + &injPhdr); + + if (!injPhdr) { + ELFU_WARN("insertSection: Could not find a place to insert section.\n"); + goto ERROR; + } + + ELFU_INFO("Inserting %s at address 0x%jx...\n", + elfu_mScnName(mrel, oldscn), + injAddr); + + injOffset = injAddr - injPhdr->phdr.p_vaddr + injPhdr->phdr.p_offset; + + newscn->shdr.sh_addr = injAddr; + newscn->shdr.sh_offset = injOffset; + + if (CIRCLEQ_EMPTY(&injPhdr->childScnList) + || CIRCLEQ_LAST(&injPhdr->childScnList)->shdr.sh_offset < injOffset) { + CIRCLEQ_INSERT_TAIL(&injPhdr->childScnList, newscn, elemChildScn); + } else { + ElfuScn *ms; + CIRCLEQ_FOREACH(ms, &injPhdr->childScnList, elemChildScn) { + if (injOffset < ms->shdr.sh_offset) { + CIRCLEQ_INSERT_BEFORE(&injPhdr->childScnList, ms, newscn, elemChildScn); + break; + } + } + } + + + /* Inject name */ + if (me->shstrtab) { + char *newname; + size_t newnamelen; + + newnamelen = strlen("reladd") + 1; + if (elfu_mScnName(mrel, oldscn)) { + newnamelen += strlen(elfu_mScnName(mrel, oldscn)); + } + + newname = malloc(newnamelen); + strcpy(newname, "reladd"); + strcat(newname, elfu_mScnName(mrel, oldscn)); + + if (!newname) { + ELFU_WARN("insertSection: malloc() failed for newname. Leaving section name empty.\n"); + newscn->shdr.sh_name = 0; + } else { + size_t offset = me->shstrtab->shdr.sh_size; + + if (!appendData(me->shstrtab, newname, newnamelen)) { + newscn->shdr.sh_name = offset; + } + + free(newname); + } + } + + return newscn; + } else { + ELFU_WARN("insertSection: Skipping non-memory section %s (type %d flags %jd).\n", + elfu_mScnName(mrel, oldscn), + oldscn->shdr.sh_type, + oldscn->shdr.sh_flags); + goto ERROR; + } + + ERROR: + if (newscn) { + // TODO: Destroy newscn + } + return NULL; +} + + +static void* subScnAdd1(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2) +{ + (void)aux2; + ElfuElf *me = (ElfuElf*)aux1; + + ElfuScn *newscn; + + switch(ms->shdr.sh_type) { + case SHT_PROGBITS: /* 1 */ + case SHT_NOBITS: /* 8 */ + /* Ignore empty sections */ + if (ms->shdr.sh_size == 0) { + break; + } + + /* Find a place where it belongs and shove it in. */ + newscn = insertSection(me, mrel, ms); + if (!newscn) { + ELFU_WARN("mReladd: Could not insert section %s (type %d), skipping.\n", + elfu_mScnName(mrel, ms), + ms->shdr.sh_type); + } + break; + } + + return NULL; +} + + +static void* subScnAdd2(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2) +{ + (void)aux2; + ElfuElf *me = (ElfuElf*)aux1; + (void)me; + + switch(ms->shdr.sh_type) { + case SHT_NULL: /* 0 */ + case SHT_PROGBITS: /* 1 */ + case SHT_STRTAB: /* 3 */ + case SHT_NOBITS: /* 8 */ + break; + + + case SHT_REL: /* 9 */ + /* Relocate. */ + elfu_mRelocate32(me, elfu_mScnByOldscn(me, ms->infoptr), ms); + break; + + case SHT_RELA: /* 4 */ + // TODO: Needs a parser + //elfu_mRelocate(elfu_mScnByOldscn(me, ms->infoptr), ms); + + case SHT_SYMTAB: /* 2 */ + /* Merge with the existing table. Take care of string tables also. */ + + /* The next section types either do not occur in .o files, or are + * not strictly necessary to process here. */ + case SHT_NOTE: /* 7 */ + case SHT_HASH: /* 5 */ + case SHT_DYNAMIC: /* 6 */ + case SHT_SHLIB: /* 10 */ + case SHT_DYNSYM: /* 11 */ + case SHT_INIT_ARRAY: /* 14 */ + case SHT_FINI_ARRAY: /* 15 */ + case SHT_PREINIT_ARRAY: /* 16 */ + case SHT_GROUP: /* 17 */ + case SHT_SYMTAB_SHNDX: /* 18 */ + case SHT_NUM: /* 19 */ + default: + ELFU_WARN("mReladd: Skipping section %s (type %d).\n", + elfu_mScnName(mrel, ms), + ms->shdr.sh_type); + } + + return NULL; +} + + +void elfu_mReladd(ElfuElf *me, ElfuElf *mrel) +{ + assert(me); + assert(mrel); + + /* For each section in object file, guess how to insert it */ + elfu_mScnForall(mrel, subScnAdd1, me, NULL); + + /* Do relocations and other stuff */ + elfu_mScnForall(mrel, subScnAdd2, me, NULL); + + /* Re-layout to accommodate new contents */ + elfu_mLayoutAuto(me); +} diff --git a/src/modelops/relocate.c b/src/modelops/relocate.c new file mode 100644 index 0000000..972bda3 --- /dev/null +++ b/src/modelops/relocate.c @@ -0,0 +1,169 @@ +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <libelfu/libelfu.h> + + +static void* subFindByName(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2) +{ + char *name = (char*)aux1; + (void)aux2; + + if (elfu_mScnName(me, ms)) { + if (!strcmp(elfu_mScnName(me, ms), name)) { + return ms; + } + } + + /* Continue */ + return NULL; +} + +/* Hazard a guess where a function may be found in the PLT */ +static GElf_Word pltLookupVal(ElfuElf *metarget, char *name) +{ + ElfuScn *relplt; + ElfuScn *plt; + ElfuRel *rel; + GElf_Word j; + + relplt = elfu_mScnForall(metarget, subFindByName, ".rel.plt", NULL); + if (!relplt) { + ELFU_WARN("dynsymLookupVal: Could not find .rel.plt section in destination ELF.\n"); + return 0; + } + + plt = elfu_mScnForall(metarget, subFindByName, ".plt", NULL); + if (!plt) { + ELFU_WARN("dynsymLookupVal: Could not find .plt section in destination ELF.\n"); + return 0; + } + + + /* Look up name */ + assert(relplt->reltab); + assert(relplt->linkptr); + assert(relplt->linkptr->symtab); + j = 0; + CIRCLEQ_FOREACH(rel, &relplt->reltab->rels, elem) { + GElf_Word i; + ElfuSym *sym; + + j++; + + if (rel->type != R_386_JMP_SLOT) { + continue; + } + + sym = CIRCLEQ_FIRST(&relplt->linkptr->symtab->syms); + for (i = 1; i < rel->sym; i++) { + sym = CIRCLEQ_NEXT(sym, elem); + } + + if (!sym->name) { + continue; + } + + if (!strcmp(sym->name, name)) { + /* If this is the symbol we are looking for, then in an x86 binary + * the jump to the dynamic symbol is probably at offset (j * 16) + * from the start of the PLT, where j is the PLT entry and 16 is + * the number of bytes the machine code in a PLT entry take. */ + GElf_Addr addr = plt->shdr.sh_addr + (16 * j); + ELFU_DEBUG("dynsymLookupVal: Guessing symbol '%s' is in destination memory at %jx (PLT entry #%d).\n", name, addr, j); + return addr; + } + } + + ELFU_WARN("dynsymLookupVal: Could not find symbol '%s' in destination ELF.\n", name); + + return 0; +} + + +static GElf_Word symtabLookupVal(ElfuElf *metarget, ElfuScn *msst, GElf_Word entry) +{ + GElf_Word i; + ElfuSym *sym; + + assert(metarget); + assert(msst); + assert(msst->symtab); + assert(entry > 0); + assert(!CIRCLEQ_EMPTY(&msst->symtab->syms)); + + sym = CIRCLEQ_FIRST(&msst->symtab->syms); + for (i = 1; i < entry; i++) { + sym = CIRCLEQ_NEXT(sym, elem); + } + + switch (sym->type) { + case STT_NOTYPE: + case STT_OBJECT: + case STT_FUNC: + if (sym->scnptr) { + assert(elfu_mScnByOldscn(metarget, sym->scnptr)); + return elfu_mScnByOldscn(metarget, sym->scnptr)->shdr.sh_addr + sym->value; + } else if (sym->shndx == SHN_UNDEF) { + /* Look the symbol up in .dyn.plt. If it cannot be found there then + * .rel.dyn may need to be expanded with a COPY relocation so the + * dynamic linker fixes up the (TODO). */ + return pltLookupVal(metarget, sym->name); + } else if (sym->shndx == SHN_ABS) { + return sym->value; + } else { + ELFU_WARN("symtabLookupVal: Symbol binding COMMON is not supported, using 0.\n"); + return 0; + } + break; + case STT_SECTION: + assert(sym->scnptr); + assert(elfu_mScnByOldscn(metarget, sym->scnptr)); + return elfu_mScnByOldscn(metarget, sym->scnptr)->shdr.sh_addr; + case STT_FILE: + ELFU_WARN("symtabLookupVal: Symbol type FILE is not supported, using 0.\n"); + return 0; + default: + ELFU_WARN("symtabLookupVal: Unknown symbol type %d for %s.\n", sym->type, sym->name); + return 0; + } +} + +void elfu_mRelocate32(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt) +{ + ElfuRel *rel; + + assert(mstarget); + assert(msrt); + + ELFU_DEBUG("Relocating in section of type %d size %jx\n", + mstarget->shdr.sh_type, + mstarget->shdr.sh_size); + + CIRCLEQ_FOREACH(rel, &msrt->reltab->rels, elem) { + Elf32_Word *dest = (Elf32_Word*)(((char*)(mstarget->data.d_buf)) + rel->offset); + Elf32_Word a = rel->addendUsed ? rel->addend : *dest; + Elf32_Addr p = mstarget->shdr.sh_addr + rel->offset; + Elf32_Addr s = symtabLookupVal(metarget, msrt->linkptr, rel->sym); + Elf32_Word newval = *dest; + + switch(rel->type) { + case R_386_NONE: + ELFU_DEBUG("Skipping relocation: R_386_NONE"); + break; + case R_386_32: + ELFU_DEBUG("Relocation: R_386_32"); + newval = s + a; + break; + case R_386_PC32: + ELFU_DEBUG("Relocation: R_386_PC32"); + newval = s + a - p; + break; + + default: + ELFU_DEBUG("Skipping relocation: Unknown type %d", rel->type); + } + ELFU_DEBUG(", overwriting %x with %x.\n", *dest, newval); + *dest = newval; + } +} diff --git a/src/modelops/section.c b/src/modelops/section.c new file mode 100644 index 0000000..a96377c --- /dev/null +++ b/src/modelops/section.c @@ -0,0 +1,193 @@ +#include <assert.h> +#include <stdlib.h> +#include <libelfu/libelfu.h> + + +/* Meta-functions */ + +void* elfu_mScnForall(ElfuElf *me, SectionHandlerFunc f, void *aux1, void *aux2) +{ + ElfuPhdr *mp; + ElfuScn *ms; + + // TODO: Sort PHDRs by offset before interating + + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + if (mp->phdr.p_type != PT_LOAD) { + continue; + } + + CIRCLEQ_FOREACH(ms, &mp->childScnList, elemChildScn) { + void *rv = f(me, ms, aux1, aux2); + + if (rv) { + return rv; + } + } + } + + CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) { + void *rv = f(me, ms, aux1, aux2); + + if (rv) { + return rv; + } + } + + return NULL; +} + + + + +/* Counting */ + +static void* subCounter(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2) +{ + size_t *i = (size_t*)aux1; + ElfuScn *otherScn = (ElfuScn*)aux2; + + if (ms == otherScn) { + return ms; + } + + *i += 1; + + /* Continue */ + return NULL; +} + + +size_t elfu_mScnCount(ElfuElf *me) +{ + /* NULL section *is not* counted */ + size_t i = 0; + + assert(me); + + elfu_mScnForall(me, subCounter, &i, NULL); + + return i; +} + + +/* Returns the index a section would have in the flattened ELF */ +size_t elfu_mScnIndex(ElfuElf *me, ElfuScn *ms) +{ + /* NULL section *is* counted */ + size_t i = 1; + + assert(me); + assert(ms); + + elfu_mScnForall(me, subCounter, &i, ms); + + /* If this assertion is broken then ms is not a section in me. */ + assert(i <= elfu_mScnCount(me)); + return i; +} + + +static void* subOldscn(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2) +{ + ElfuScn *otherScn = (ElfuScn*)aux1; + (void)aux2; + + if (ms->oldptr == otherScn) { + return ms; + } + + /* Continue */ + return NULL; +} + +/* Returns the section with oldscn == oldscn */ +ElfuScn* elfu_mScnByOldscn(ElfuElf *me, ElfuScn *oldscn) +{ + assert(me); + assert(oldscn); + + return elfu_mScnForall(me, subOldscn, oldscn, NULL); +} + + + + +/* Convenience */ + +char* elfu_mScnName(ElfuElf *me, ElfuScn *ms) +{ + assert(me); + assert(ms); + + if (!me->shstrtab) { + return NULL; + } + + if (!me->shstrtab->data.d_buf) { + return NULL; + } + + return &((char*)me->shstrtab->data.d_buf)[ms->shdr.sh_name]; +} + + +static void* subScnsToArray(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2) +{ + ElfuScn **arr = (ElfuScn**)aux1; + size_t *i = (size_t*)aux2; + + arr[(*i)] = ms; + *i += 1; + + /* Continue */ + return NULL; +} + +static int cmpScnOffs(const void *ms1, const void *ms2) +{ + assert(ms1); + assert(ms2); + + ElfuScn *s1 = *(ElfuScn**)ms1; + ElfuScn *s2 = *(ElfuScn**)ms2; + + assert(s1); + assert(s2); + + + if (s1->shdr.sh_offset < s2->shdr.sh_offset) { + return -1; + } else if (s1->shdr.sh_offset == s2->shdr.sh_offset) { + return 0; + } else /* if (s1->shdr.sh_offset > s2->shdr.sh_offset) */ { + return 1; + } +} + +ElfuScn** elfu_mScnSortedByOffset(ElfuElf *me, size_t *count) +{ + assert(me); + + size_t numSecs; + ElfuScn **sortedSecs; + size_t i; + + /* Sort sections by offset in file */ + numSecs = elfu_mScnCount(me); + sortedSecs = malloc(numSecs * sizeof(*sortedSecs)); + if (!sortedSecs) { + ELFU_WARN("elfu_mScnSortedByOffset: Failed to allocate memory.\n"); + return NULL; + } + + i = 0; + elfu_mScnForall(me, subScnsToArray, sortedSecs, &i); + assert(i == numSecs); + + qsort(sortedSecs, numSecs, sizeof(*sortedSecs), cmpScnOffs); + + *count = numSecs; + + return sortedSecs; +} diff --git a/src/modelops/toFile.c b/src/modelops/toFile.c new file mode 100644 index 0000000..da06f4a --- /dev/null +++ b/src/modelops/toFile.c @@ -0,0 +1,115 @@ +#include <stdlib.h> +#include <libelfu/libelfu.h> + + + +static void modelToPhdrs(ElfuElf *me, Elf *e) +{ + ElfuPhdr *mp; + size_t i; + + /* Count PHDRs */ + i = 0; + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + i++; + } + + if (!gelf_newphdr(e, i)) { + ELFU_WARNELF("gelf_newphdr"); + } + + /* Copy PHDRs */ + i = 0; + CIRCLEQ_FOREACH(mp, &me->phdrList, elem) { + if (!gelf_update_phdr (e, i, &mp->phdr)) { + ELFU_WARNELF("gelf_update_phdr"); + } + + i++; + } +} + + + +static void* modelToSection(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2) +{ + (void) me; + (void) aux2; + Elf *e = (Elf*)aux1; + Elf_Scn *scnOut; + + scnOut = elf_newscn(e); + if (!scnOut) { + ELFU_WARNELF("elf_newscn"); + return (void*)-1; + } + + + /* SHDR */ + if (ms->linkptr) { + ms->shdr.sh_link = elfu_mScnIndex(me, ms->linkptr); + } + if (ms->infoptr) { + ms->shdr.sh_info = elfu_mScnIndex(me, ms->infoptr); + } + if (!gelf_update_shdr(scnOut, &ms->shdr)) { + ELFU_WARNELF("gelf_update_shdr"); + } + + + /* Data */ + if (ms->data.d_buf) { + Elf_Data *dataOut = elf_newdata(scnOut); + if (!dataOut) { + ELFU_WARNELF("elf_newdata"); + } + + dataOut->d_align = ms->data.d_align; + dataOut->d_buf = ms->data.d_buf; + dataOut->d_off = ms->data.d_off; + dataOut->d_type = ms->data.d_type; + dataOut->d_size = ms->data.d_size; + dataOut->d_version = ms->data.d_version; + } + + return NULL; +} + + + + + +void elfu_mToElf(ElfuElf *me, Elf *e) +{ + /* We control the ELF file's layout now. */ + /* tired's libelf also offers ELF_F_LAYOUT_OVERLAP for overlapping sections, + * but we don't want that since we filtered it out in the reading stage + * already. It would be too mind-blowing to handle the dependencies between + * the PHDRs and sections then... */ + elf_flagelf(e, ELF_C_SET, ELF_F_LAYOUT); + + + /* EHDR */ + if (!gelf_newehdr(e, me->elfclass)) { + ELFU_WARNELF("gelf_newehdr"); + } + + if (me->shstrtab) { + me->ehdr.e_shstrndx = elfu_mScnIndex(me, me->shstrtab); + } + + if (!gelf_update_ehdr(e, &me->ehdr)) { + ELFU_WARNELF("gelf_update_ehdr"); + } + + + /* Sections */ + elfu_mScnForall(me, modelToSection, e, NULL); + + + /* PHDRs */ + modelToPhdrs(me, e); + + + elf_flagelf(e, ELF_C_SET, ELF_F_DIRTY); +} |