diff options
Diffstat (limited to 'src/libelfu')
-rw-r--r-- | src/libelfu/elfops/check.c | 202 | ||||
-rw-r--r-- | src/libelfu/modelops/check.c | 54 | ||||
-rw-r--r-- | src/libelfu/modelops/clone.c | 41 | ||||
-rw-r--r-- | src/libelfu/modelops/detour.c | 51 | ||||
-rw-r--r-- | src/libelfu/modelops/dump.c | 239 | ||||
-rw-r--r-- | src/libelfu/modelops/fromFile.c | 490 | ||||
-rw-r--r-- | src/libelfu/modelops/layout.c | 324 | ||||
-rw-r--r-- | src/libelfu/modelops/phdr.c | 36 | ||||
-rw-r--r-- | src/libelfu/modelops/reladd.c | 323 | ||||
-rw-r--r-- | src/libelfu/modelops/relocate.c | 70 | ||||
-rw-r--r-- | src/libelfu/modelops/section.c | 196 | ||||
-rw-r--r-- | src/libelfu/modelops/symtab.c | 269 | ||||
-rw-r--r-- | src/libelfu/modelops/toFile.c | 121 |
13 files changed, 2416 insertions, 0 deletions
diff --git a/src/libelfu/elfops/check.c b/src/libelfu/elfops/check.c new file mode 100644 index 0000000..e184b26 --- /dev/null +++ b/src/libelfu/elfops/check.c @@ -0,0 +1,202 @@ +#include <assert.h> +#include <stdlib.h> +#include <libelfu/libelfu.h> + +int elfu_eCheck(Elf *e) +{ + int elfclass; + GElf_Ehdr ehdr; + GElf_Phdr *phdrs = NULL; + GElf_Shdr *shdrs = NULL; + size_t i, j, numPhdr, numShdr; + int retval = 0; + + assert(e); + + elfclass = gelf_getclass(e); + if (elfclass == ELFCLASSNONE) { + ELFU_WARNELF("getclass"); + goto ERROR; + } + + if (!gelf_getehdr(e, &ehdr)) { + ELFU_WARNELF("gelf_getehdr"); + goto ERROR; + } + + if (ehdr.e_machine != EM_386 && ehdr.e_machine != EM_X86_64) { + ELFU_WARN("Sorry, only x86-32 and x86-64 ELF files are supported at the moment.\n"); + goto ERROR; + } + + if (elf_getphdrnum(e, &numPhdr)) { + ELFU_WARNELF("elf_getphdrnum"); + goto ERROR; + } + + if (elf_getshdrnum(e, &numShdr)) { + ELFU_WARNELF("elf_getshdrnum"); + goto ERROR; + } + + + if (numPhdr > 0) { + phdrs = malloc(numPhdr * sizeof(GElf_Phdr)); + if (!phdrs) { + ELFU_WARN("elfu_eCheck: malloc() failed for phdrs.\n"); + goto ERROR; + } + + /* Attempt to load all PHDRs at once to catch any errors early */ + for (i = 0; i < numPhdr; i++) { + GElf_Phdr phdr; + if (gelf_getphdr(e, i, &phdr) != &phdr) { + ELFU_WARN("gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1)); + goto ERROR; + } + + phdrs[i] = phdr; + } + + /* Check that LOAD PHDR memory ranges do not overlap, and that others + * are either fully contained in a LOAD range, or not at all. */ + for (i = 0; i < numPhdr; i++) { + if (phdrs[i].p_type != PT_LOAD) { + continue; + } + + for (j = 0; j < numPhdr; j++) { + if (j == i || phdrs[j].p_type != PT_LOAD) { + continue; + } + + if (OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz, + phdrs[j].p_vaddr, phdrs[j].p_memsz)) { + if (phdrs[j].p_type == PT_LOAD) { + ELFU_WARN("elfu_eCheck: Found LOAD PHDRs that overlap in memory.\n"); + goto ERROR; + } else if (!FULLY_OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz, + phdrs[j].p_vaddr, phdrs[j].p_memsz)) { + ELFU_WARN("elfu_eCheck: PHDRs %d and %d partially overlap in memory.\n", i, j); + goto ERROR; + } + } + } + } + } + + + if (numShdr > 1) { + /* SHDRs should not overlap with PHDRs. */ + if (OVERLAPPING(ehdr.e_shoff, numShdr * ehdr.e_shentsize, + ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) { + ELFU_WARN("elfu_eCheck: SHDRs overlap with PHDRs.\n"); + goto ERROR; + } + + shdrs = malloc(numShdr * sizeof(GElf_Shdr)); + if (!shdrs) { + ELFU_WARN("elfu_eCheck: malloc() failed for shdrs.\n"); + goto ERROR; + } + + /* Attempt to load all SHDRs at once to catch any errors early */ + for (i = 1; i < numShdr; i++) { + Elf_Scn *scn; + GElf_Shdr shdr; + + scn = elf_getscn(e, i); + if (!scn) { + ELFU_WARN("elf_getscn() failed for #%d: %s\n", i, elf_errmsg(-1)); + } + + if (gelf_getshdr(scn, &shdr) != &shdr) { + ELFU_WARNELF("gelf_getshdr"); + goto ERROR; + } + + shdrs[i] = shdr; + } + + + /* Check that Section memory ranges do not overlap. + * NB: Section 0 is reserved and thus ignored. */ + for (i = 1; i < numShdr; i++) { + /* Section should not overlap with EHDR. */ + if (shdrs[i].sh_offset == 0) { + ELFU_WARN("elfu_eCheck: Section %d overlaps with EHDR.\n", i); + goto ERROR; + } + + /* Section should not overlap with PHDRs. */ + if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]), + ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) { + ELFU_WARN("elfu_eCheck: Section %d overlaps with PHDR.\n", i); + goto ERROR; + } + + /* Section should not overlap with SHDRs. */ + if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]), + ehdr.e_shoff, numShdr * ehdr.e_shentsize)) { + ELFU_WARN("elfu_eCheck: Section %d overlaps with SHDRs.\n", i); + goto ERROR; + } + + for (j = 1; j < numShdr; j++) { + if (j == i) { + continue; + } + + /* Sections must not overlap in memory. */ + if (shdrs[i].sh_addr != 0 + && shdrs[j].sh_addr != 0 + && OVERLAPPING(shdrs[i].sh_addr, shdrs[i].sh_size, + shdrs[j].sh_addr, shdrs[j].sh_size)) { + ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in memory.\n", i, j); + goto ERROR; + } + + /* Sections must not overlap in file. */ + if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]), + shdrs[j].sh_offset, SCNFILESIZE(&shdrs[j]))) { + ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in file.\n", i, j); + goto ERROR; + } + } + + /* Section addr/offset should match parent PHDR. + * Find parent PHDR: */ + for (j = 0; j < numPhdr; j++) { + if (PHDR_CONTAINS_SCN_IN_MEMORY(&phdrs[j], &shdrs[i])) { + GElf_Off shoff = phdrs[j].p_offset + (shdrs[i].sh_addr - phdrs[j].p_vaddr); + + if (shdrs[i].sh_offset != shoff + || !PHDR_CONTAINS_SCN_IN_FILE(&phdrs[j], &shdrs[i])) { + ELFU_WARN("elfu_eCheck: Memory/file offsets/sizes are not congruent for SHDR %d, PHDR %d.\n", i, j); + goto ERROR; + } + } + } + + /* sh_link members should not point to sections out of range. */ + if (shdrs[i].sh_link >= numShdr) { + ELFU_WARN("elfu_eCheck: Bogus sh_link in SHDR %d.\n", i); + } + } + } + + + DONE: + if (phdrs) { + free(phdrs); + } + if (shdrs) { + free(shdrs); + } + return retval; + + ERROR: + ELFU_WARN("elfu_eCheck: Errors found.\n"); + retval = -1; + goto DONE; +} diff --git a/src/libelfu/modelops/check.c b/src/libelfu/modelops/check.c new file mode 100644 index 0000000..5234bef --- /dev/null +++ b/src/libelfu/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/libelfu/modelops/clone.c b/src/libelfu/modelops/clone.c new file mode 100644 index 0000000..8f92919 --- /dev/null +++ b/src/libelfu/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; + + CIRCLEQ_INIT(&ms->symtab.syms); + CIRCLEQ_INIT(&ms->reltab.rels); + + return newscn; +} diff --git a/src/libelfu/modelops/detour.c b/src/libelfu/modelops/detour.c new file mode 100644 index 0000000..075d945 --- /dev/null +++ b/src/libelfu/modelops/detour.c @@ -0,0 +1,51 @@ +#include <libelfu/libelfu.h> +#include <string.h> + +static void* subFindByAddr(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2) +{ + GElf_Addr a = *(GElf_Addr*)aux1; + + if (OVERLAPPING(ms->shdr.sh_addr, ms->shdr.sh_size, a, 1)) { + return ms; + } + + /* Continue */ + return NULL; +} + + +void elfu_mDetour(ElfuElf *me, GElf_Addr from, GElf_Addr to) +{ + ElfuScn *ms; + GElf_Word scnoffset; + unsigned char detourcode[] = {0xe9, 0xfc, 0xff, 0xff, 0xff}; + + ms = elfu_mScnForall(me, subFindByAddr, &from, NULL); + + if (!ms) { + ELFU_WARN("mDetour: Cannot find address %x in any section.\n", + (unsigned)from); + return; + } + + if (ms->shdr.sh_type != SHT_PROGBITS) { + ELFU_WARN("mDetour: Cannot detour in non-PROGBITS section %s.\n", + elfu_mScnName(me, ms)); + return; + } + + scnoffset = from - ms->shdr.sh_addr; + + if (ms->shdr.sh_size - scnoffset < 5) { + ELFU_WARN("mDetour: Not enough space to insert a detour.\n"); + return; + } + + ELFU_DEBUG("mDetour: Detouring at address %x in section %s to %x.\n", + (unsigned)from, + elfu_mScnName(me, ms), + (unsigned)to); + + *(Elf32_Word*)(detourcode + 1) = to - from - 5; + memcpy((char*)ms->data.d_buf + scnoffset, detourcode, 5); +} diff --git a/src/libelfu/modelops/dump.c b/src/libelfu/modelops/dump.c new file mode 100644 index 0000000..b2e172e --- /dev/null +++ b/src/libelfu/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 %8x %8x %8x %8x %8x %8x %8x %8x\n", + segmentTypeStr(mp->phdr.p_type), + (unsigned)mp->phdr.p_type, + (unsigned)mp->phdr.p_offset, + (unsigned)mp->phdr.p_vaddr, + (unsigned)mp->phdr.p_paddr, + (unsigned)mp->phdr.p_filesz, + (unsigned)mp->phdr.p_memsz, + (unsigned)mp->phdr.p_flags, + (unsigned)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 @ %8x\n", + segmentTypeStr(mpc->phdr.p_type), + (unsigned)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 @ %8x\n", + elfu_mScnName(me, msc), + (unsigned)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 %8x %9x %8x %2x %2x %2d %-17s %-17s\n", + namestr, + typestr, + (unsigned)ms->shdr.sh_addr, + (unsigned)ms->shdr.sh_offset, + (unsigned)ms->shdr.sh_size, + (unsigned)ms->shdr.sh_entsize, + (unsigned)ms->shdr.sh_flags, + (unsigned)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 %8x\n", (unsigned)me->ehdr.e_entry); + ELFU_INFO(" e_phoff %8x\n", (unsigned)me->ehdr.e_phoff); + ELFU_INFO(" e_shoff %8x\n", (unsigned)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 @ %8x\n", + elfu_mScnName(me, ms), + (unsigned)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/libelfu/modelops/fromFile.c b/src/libelfu/modelops/fromFile.c new file mode 100644 index 0000000..dd8b462 --- /dev/null +++ b/src/libelfu/modelops/fromFile.c @@ -0,0 +1,490 @@ +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> +#include <libelfu/libelfu.h> + + +static void parseSymtab(ElfuElf *me, ElfuScn *ms, ElfuScn**origScnArr) +{ + ElfuSym *sym; + size_t i; + + assert(ms); + assert(ms->data.d_buf); + assert(origScnArr); + + /* Parse symbols from their elfclass-specific format */ + if (me->elfclass == ELFCLASS32) { + for (i = 1; (i + 1) * sizeof(Elf32_Sym) <= ms->shdr.sh_size; i++) { + Elf32_Sym *cursym = ((Elf32_Sym*)ms->data.d_buf) + i; + ElfuSym *newsym = malloc(sizeof(*sym)); + assert(newsym); + + newsym->name = cursym->st_name; + newsym->value = cursym->st_value; + newsym->size = cursym->st_size; + newsym->bind = ELF32_ST_BIND(cursym->st_info); + newsym->type = ELF32_ST_TYPE(cursym->st_info); + newsym->other = cursym->st_other; + newsym->shndx = cursym->st_shndx; + + + + CIRCLEQ_INSERT_TAIL(&ms->symtab.syms, newsym, elem); + } + } else if (me->elfclass == ELFCLASS64) { + for (i = 1; (i + 1) * sizeof(Elf64_Sym) <= ms->shdr.sh_size; i++) { + Elf64_Sym *cursym = ((Elf64_Sym*)ms->data.d_buf) + i; + ElfuSym *newsym = malloc(sizeof(*sym)); + assert(newsym); + + newsym->name = cursym->st_name; + newsym->value = cursym->st_value; + newsym->size = cursym->st_size; + newsym->bind = ELF64_ST_BIND(cursym->st_info); + newsym->type = ELF64_ST_TYPE(cursym->st_info); + newsym->other = cursym->st_other; + newsym->shndx = cursym->st_shndx; + + + + CIRCLEQ_INSERT_TAIL(&ms->symtab.syms, newsym, elem); + } + } else { + /* Unknown elfclass */ + assert(0); + } + + /* For each section, find the section it points to if any. */ + CIRCLEQ_FOREACH(sym, &ms->symtab.syms, elem) { + switch (sym->shndx) { + case SHN_UNDEF: + case SHN_ABS: + case SHN_COMMON: + sym->scnptr = NULL; + break; + default: + sym->scnptr = origScnArr[sym->shndx - 1]; + break; + } + } +} + + +static void parseReltab32(ElfuScn *ms) +{ + size_t i; + + assert(ms); + assert(ms->data.d_buf); + + + 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(&ms->reltab.rels, rel, elem); + } +} + + +static void parseRelatab64(ElfuScn *ms) +{ + size_t i; + + assert(ms); + assert(ms->data.d_buf); + + + for (i = 0; (i + 1) * sizeof(Elf64_Rela) <= ms->shdr.sh_size; i++) { + Elf64_Rela *currel = &(((Elf64_Rela*)ms->data.d_buf)[i]); + ElfuRel *rel; + + rel = malloc(sizeof(*rel)); + assert(rel); + + rel->offset = currel->r_offset; + rel->sym = ELF64_R_SYM(currel->r_info); + rel->type = ELF64_R_TYPE(currel->r_info); + rel->addendUsed = 1; + rel->addend = currel->r_addend; + + CIRCLEQ_INSERT_TAIL(&ms->reltab.rels, rel, elem); + } +} + + +static int cmpScnOffs(const void *ms1, const void *ms2) +{ + ElfuScn *s1 = *(ElfuScn**)ms1; + ElfuScn *s2 = *(ElfuScn**)ms2; + + assert(ms1); + assert(ms2); + + s1 = *(ElfuScn**)ms1; + 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 (%x bytes).\n", (unsigned)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((char*)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; + + CIRCLEQ_INIT(&ms->symtab.syms); + CIRCLEQ_INIT(&ms->reltab.rels); + + + 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->symtab = 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: + me->symtab = ms; + case SHT_DYNSYM: + parseSymtab(me, ms, secArray); + 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) { + parseReltab32(ms); + } else if (me->elfclass == ELFCLASS64) { + /* Not used on x86-64 */ + assert(0); + } + break; + case SHT_RELA: + if (me->elfclass == ELFCLASS32) { + /* Not used on x86-32 */ + assert(0); + } else if (me->elfclass == ELFCLASS64) { + parseRelatab64(ms); + } + 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/libelfu/modelops/layout.c b/src/libelfu/modelops/layout.c new file mode 100644 index 0000000..8abc766 --- /dev/null +++ b/src/libelfu/modelops/layout.c @@ -0,0 +1,324 @@ +#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%x...\n", (unsigned)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 %u bytes for NOBITS expansion. Data may be inconsistent.\n", + (unsigned)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) +{ + ElfuPhdr *p1; + ElfuPhdr *p2; + + assert(mp1); + assert(mp2); + + p1 = *(ElfuPhdr**)mp1; + 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/libelfu/modelops/phdr.c b/src/libelfu/modelops/phdr.c new file mode 100644 index 0000000..d26eb77 --- /dev/null +++ b/src/libelfu/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/libelfu/modelops/reladd.c b/src/libelfu/modelops/reladd.c new file mode 100644 index 0000000..bc909e4 --- /dev/null +++ b/src/libelfu/modelops/reladd.c @@ -0,0 +1,323 @@ +#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) +{ + char *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%x...\n", + elfu_mScnName(mrel, oldscn), + (unsigned)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 %u).\n", + elfu_mScnName(mrel, oldscn), + oldscn->shdr.sh_type, + (unsigned)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) +{ + ElfuScn *newscn; + ElfuElf *me = (ElfuElf*)aux1; + (void)aux2; + + + 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) +{ + ElfuElf *me = (ElfuElf*)aux1; + (void)aux2; + + 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_mRelocate(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; +} + + + +static void insertSymClone(ElfuElf *me, const ElfuScn *oldmsst, const ElfuSym *oldsym) +{ + GElf_Xword newsize; + char *newbuf; + ElfuScn *newscn = NULL; + ElfuSym *newsym; + char *oldsymname; + + assert(me); + assert(oldmsst); + assert(oldsym); + + /* If the old symbol pointed to a section, find its clone in the target */ + if (oldsym->scnptr) { + newscn = elfu_mScnByOldscn(me, oldsym->scnptr); + + /* If we didn't copy the section referenced, we won't + * copy this symbol either */ + if (!newscn) { + return; + } + } + + // TODO: Allocate symtab if none present + assert(me->symtab); + + /* Allocate memory for the cloned symbol */ + newsym = malloc(sizeof(*newsym)); + if (!newsym) { + ELFU_WARN("insertSymClone: malloc() failed for newsym.\n"); + goto ERROR; + } + + oldsymname = ELFU_SYMSTR(oldmsst, oldsym->name); + + /* Expand .strtab, append symbol name, link newsym to it */ + newsize = me->symtab->linkptr->shdr.sh_size + strlen(oldsymname) + 1; + newbuf = realloc(me->symtab->linkptr->data.d_buf, newsize); + if (!newbuf) { + ELFU_WARN("insertSymClone: realloc() failed for strtab.\n"); + goto ERROR; + } + + me->symtab->linkptr->data.d_buf = newbuf; + + newsym->name = me->symtab->linkptr->shdr.sh_size; + + strcpy(newbuf + newsym->name, oldsymname); + + me->symtab->linkptr->data.d_size = newsize; + me->symtab->linkptr->shdr.sh_size = newsize; + + + /* Copy all other fields */ + newsym->scnptr = newscn; + newsym->shndx = oldsym->shndx; /* If scnptr == NULL, this becomes relevant */ + newsym->bind = oldsym->bind; + newsym->other = oldsym->other; + newsym->size = oldsym->size; + newsym->type = oldsym->type; + newsym->value = oldsym->value; + + /* In executables, symbol addresses need to be in memory */ + if (newscn) { + newsym->value += newscn->shdr.sh_addr; + } + + /* Insert symbol */ + CIRCLEQ_INSERT_TAIL(&me->symtab->symtab.syms, newsym, elem); + + return; + + ERROR: + if (newsym) { + free(newsym); + } +} + +static void mergeSymtab(ElfuElf *me, const ElfuElf *mrel) +{ + ElfuSym *sym; + + assert(me); + assert(mrel); + + CIRCLEQ_FOREACH(sym, &mrel->symtab->symtab.syms, elem) { + insertSymClone(me, mrel->symtab, sym); + } +} + + + +void elfu_mReladd(ElfuElf *me, const ElfuElf *mrel) +{ + assert(me); + assert(mrel); + + /* For each section in object file, guess how to insert it */ + elfu_mScnForall((ElfuElf*)mrel, subScnAdd1, me, NULL); + + mergeSymtab(me, mrel); + + /* Do relocations and other stuff */ + elfu_mScnForall((ElfuElf*)mrel, subScnAdd2, me, NULL); + + /* Re-layout to accommodate new contents */ + elfu_mLayoutAuto(me); +} diff --git a/src/libelfu/modelops/relocate.c b/src/libelfu/modelops/relocate.c new file mode 100644 index 0000000..eefed02 --- /dev/null +++ b/src/libelfu/modelops/relocate.c @@ -0,0 +1,70 @@ +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <libelfu/libelfu.h> + + +/* Apply relocation information from section *msrt to data in + * section *mstarget (which is stored in *metarget). */ +void elfu_mRelocate(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt) +{ + ElfuRel *rel; + + assert(mstarget); + assert(msrt); + + ELFU_DEBUG("Relocating in section of type %u size %x\n", + mstarget->shdr.sh_type, + (unsigned)mstarget->shdr.sh_size); + + CIRCLEQ_FOREACH(rel, &msrt->reltab.rels, elem) { + Elf32_Word *dest32 = (Elf32_Word*)(((char*)(mstarget->data.d_buf)) + rel->offset); + Elf64_Word *dest64 = (Elf64_Word*)(((char*)(mstarget->data.d_buf)) + rel->offset); + + + if (metarget->elfclass == ELFCLASS32) { + Elf32_Word a32 = rel->addendUsed ? rel->addend : *dest32; + Elf32_Addr p32 = mstarget->shdr.sh_addr + rel->offset; + Elf32_Addr s32 = elfu_mSymtabLookupVal(metarget, msrt->linkptr, rel->sym); + switch(rel->type) { + case R_386_NONE: + ELFU_DEBUG("Skipping relocation: R_386_NONE\n"); + break; + case R_386_32: + *dest32 = s32 + a32; + break; + case R_386_PC32: + *dest32 = s32 + a32 - p32; + break; + + default: + ELFU_DEBUG("Skipping relocation: Unknown type %d\n", rel->type); + } + } else if (metarget->elfclass == ELFCLASS64) { + Elf64_Word a64 = rel->addend; + Elf64_Addr p64 = mstarget->shdr.sh_addr + rel->offset; + Elf64_Addr s64 = elfu_mSymtabLookupVal(metarget, msrt->linkptr, rel->sym); + + /* x86-64 only uses RELA with explicit addend. */ + assert(rel->addendUsed); + + switch(rel->type) { + case R_X86_64_NONE: + ELFU_DEBUG("Skipping relocation: R_386_NONE\n"); + break; + case R_X86_64_64: + *dest64 = s64 + a64; + break; + case R_X86_64_PC32: + *dest32 = s64 + a64 - p64; + break; + case R_X86_64_32: + *dest32 = s64 + a64; + break; + + default: + ELFU_DEBUG("Skipping relocation: Unknown type %d", rel->type); + } + } + } +} diff --git a/src/libelfu/modelops/section.c b/src/libelfu/modelops/section.c new file mode 100644 index 0000000..2675126 --- /dev/null +++ b/src/libelfu/modelops/section.c @@ -0,0 +1,196 @@ +#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) +{ + ElfuScn *s1; + ElfuScn *s2; + + assert(ms1); + assert(ms2); + + s1 = *(ElfuScn**)ms1; + 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) +{ + size_t numSecs; + ElfuScn **sortedSecs; + size_t i; + + assert(me); + + /* 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/libelfu/modelops/symtab.c b/src/libelfu/modelops/symtab.c new file mode 100644 index 0000000..ef8443f --- /dev/null +++ b/src/libelfu/modelops/symtab.c @@ -0,0 +1,269 @@ +#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 *me, char *name) +{ + ElfuScn *relplt; + ElfuScn *plt; + ElfuRel *rel; + GElf_Word j; + + relplt = elfu_mScnForall(me, subFindByName, ".rel.plt", NULL); + if (!relplt) { + ELFU_WARN("dynsymLookupVal: Could not find .rel.plt section in destination ELF.\n"); + return 0; + } + + plt = elfu_mScnForall(me, subFindByName, ".plt", NULL); + if (!plt) { + ELFU_WARN("dynsymLookupVal: Could not find .plt section in destination ELF.\n"); + return 0; + } + + + /* Look up name. If the j-th entry in .rel.plt has the name we are + * looking for, we assume that the (j+1)-th entry in .plt is machine + * code to jump to the function. + * Your mileage may vary, but it works on my GNU binaries. */ + assert(relplt->linkptr); + j = 0; + CIRCLEQ_FOREACH(rel, &relplt->reltab.rels, elem) { + GElf_Word i; + ElfuSym *sym; + char *symname; + + j++; + + /* We only consider runtime relocations for functions. + * Technically, these relocations write the functions' addresses + * to the GOT, not the PLT, after the dynamic linker has found + * them. */ + if ((me->elfclass == ELFCLASS32 && rel->type != R_386_JMP_SLOT) + || (me->elfclass == ELFCLASS64 && rel->type != R_X86_64_JUMP_SLOT)) { + continue; + } + + /* Get the (rel->sym)-th symbol from the symbol table that + * .rel.plt points to. */ + sym = CIRCLEQ_FIRST(&relplt->linkptr->symtab.syms); + for (i = 1; i < rel->sym; i++) { + sym = CIRCLEQ_NEXT(sym, elem); + } + + symname = ELFU_SYMSTR(relplt->linkptr, sym->name); + if (!strcmp(symname, 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 %x (PLT entry #%u).\n", name, (unsigned)addr, j); + return addr; + } + } + + ELFU_WARN("dynsymLookupVal: Could not find symbol '%s' in destination ELF.\n", name); + + return 0; +} + + + +/* Look up a value in the symbol table section *msst which is in *me. + * If it is not found there, see if we can find it in *me's PLT. */ +GElf_Word elfu_mSymtabLookupVal(ElfuElf *me, ElfuScn *msst, GElf_Word entry) +{ + GElf_Word i; + ElfuSym *sym; + char *symname; + + assert(me); + assert(msst); + 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); + } + symname = ELFU_SYMSTR(msst, sym->name); + + switch (sym->type) { + case STT_NOTYPE: + case STT_OBJECT: + case STT_FUNC: + if (sym->scnptr) { + ElfuScn *newscn = elfu_mScnByOldscn(me, sym->scnptr); + assert(newscn); + return newscn->shdr.sh_addr + sym->value; + } else if (sym->shndx == SHN_UNDEF) { + /* Look the symbol up in .rel.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(me, symname); + } 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(me, sym->scnptr)); + return elfu_mScnByOldscn(me, 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, symname); + return 0; + } +} + + +/* Look up a value in the symbol table section *msst which is in *me. */ +GElf_Word elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name) +{ + ElfuSym *sym; + + assert(me); + assert(msst); + assert(name); + assert(strlen(name) > 0); + assert(!CIRCLEQ_EMPTY(&msst->symtab.syms)); + + CIRCLEQ_FOREACH(sym, &msst->symtab.syms, elem) { + char *symname = ELFU_SYMSTR(msst, sym->name); + + if (!strcmp(symname, name)) { + goto SYMBOL_FOUND; + } + } + return 0; + + + SYMBOL_FOUND: + + switch (sym->type) { + case STT_NOTYPE: + case STT_OBJECT: + case STT_FUNC: + if (sym->scnptr) { + GElf_Addr a = sym->value; + a += me->ehdr.e_type == ET_REL ? sym->scnptr->shdr.sh_addr : 0; + return a; + } else if (sym->shndx == SHN_UNDEF) { + return 0; + } else if (sym->shndx == SHN_ABS) { + return sym->value; + } else { + ELFU_WARN("elfu_mSymtabLookupAddrByName: Symbol binding COMMON is not supported, using 0.\n"); + return 0; + } + break; + default: + return 0; + } +} + + + +/* Convert symtab from memory model to elfclass specific format */ +void elfu_mSymtabFlatten(ElfuElf *me) +{ + ElfuSym *sym; + size_t numsyms = 0; + + elfu_mLayoutAuto(me); + + /* Update section indexes and count symbols */ + CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) { + if (sym->scnptr) { + sym->shndx = elfu_mScnIndex(me, sym->scnptr); + } + + numsyms++; + } + + /* Copy symbols to elfclass-specific format */ + if (me->elfclass == ELFCLASS32) { + size_t newsize = (numsyms + 1) * sizeof(Elf32_Sym); + size_t i; + + if (me->symtab->data.d_buf) { + free(me->symtab->data.d_buf); + } + me->symtab->data.d_buf = malloc(newsize); + assert(me->symtab->data.d_buf); + + me->symtab->data.d_size = newsize; + me->symtab->shdr.sh_size = newsize; + memset(me->symtab->data.d_buf, 0, newsize); + + i = 1; + CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) { + Elf32_Sym *es = ((Elf32_Sym*)me->symtab->data.d_buf) + i; + + es->st_name = sym->name; + es->st_value = sym->value; + es->st_size = sym->size; + es->st_info = ELF32_ST_INFO(sym->bind, sym->type); + es->st_other = sym->other; + es->st_shndx = sym->shndx; + + i++; + } + } else if (me->elfclass == ELFCLASS64) { + size_t newsize = (numsyms + 1) * sizeof(Elf64_Sym); + size_t i; + + if (me->symtab->data.d_buf) { + free(me->symtab->data.d_buf); + } + me->symtab->data.d_buf = malloc(newsize); + assert(me->symtab->data.d_buf); + + me->symtab->data.d_size = newsize; + me->symtab->shdr.sh_size = newsize; + memset(me->symtab->data.d_buf, 0, newsize); + + i = 1; + CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) { + Elf64_Sym *es = ((Elf64_Sym*)me->symtab->data.d_buf) + i; + + es->st_name = sym->name; + es->st_value = sym->value; + es->st_size = sym->size; + es->st_info = ELF64_ST_INFO(sym->bind, sym->type); + es->st_other = sym->other; + es->st_shndx = sym->shndx; + + i++; + } + } else { + /* Unknown elfclass */ + assert(0); + } + + elfu_mLayoutAuto(me); +} diff --git a/src/libelfu/modelops/toFile.c b/src/libelfu/modelops/toFile.c new file mode 100644 index 0000000..368f12a --- /dev/null +++ b/src/libelfu/modelops/toFile.c @@ -0,0 +1,121 @@ +#include <assert.h> +#include <stdlib.h> +#include <string.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) +{ + Elf_Scn *scnOut; + Elf *e = (Elf*)aux1; + (void) me; + (void) aux2; + + 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) +{ + if (me->symtab) { + elfu_mSymtabFlatten(me); + } + + + /* 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); +} |