From: norly Date: Wed, 12 Jun 2013 17:41:42 +0000 (+0100) Subject: Rename elfedit to centaur, model to modelops X-Git-Url: https://git.enpas.org/?p=centaur.git;a=commitdiff_plain;h=164b3a3a62a0577f9a28078771dd6fdc102598dc Rename elfedit to centaur, model to modelops --- diff --git a/Makefile b/Makefile index 41e8fe5..ef3548d 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PROJ := elfedit +PROJ := centaur LIBRARIES := libelf diff --git a/src/model/check.c b/src/model/check.c deleted file mode 100644 index 5234bef..0000000 --- a/src/model/check.c +++ /dev/null @@ -1,54 +0,0 @@ -#include -#include -#include -#include - - -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/model/clone.c b/src/model/clone.c deleted file mode 100644 index ca4b38f..0000000 --- a/src/model/clone.c +++ /dev/null @@ -1,41 +0,0 @@ -#include -#include -#include -#include - -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/model/dump.c b/src/model/dump.c deleted file mode 100644 index 27556f9..0000000 --- a/src/model/dump.c +++ /dev/null @@ -1,239 +0,0 @@ -#include -#include - - -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/model/fromFile.c b/src/model/fromFile.c deleted file mode 100644 index 23105e6..0000000 --- a/src/model/fromFile.c +++ /dev/null @@ -1,487 +0,0 @@ -#include -#include -#include -#include -#include - - -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/model/layout.c b/src/model/layout.c deleted file mode 100644 index e4b3fb1..0000000 --- a/src/model/layout.c +++ /dev/null @@ -1,320 +0,0 @@ -#include -#include -#include -#include - - - -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/model/phdr.c b/src/model/phdr.c deleted file mode 100644 index d26eb77..0000000 --- a/src/model/phdr.c +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include - - -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/model/reladd.c b/src/model/reladd.c deleted file mode 100644 index 2fdfb48..0000000 --- a/src/model/reladd.c +++ /dev/null @@ -1,229 +0,0 @@ -#include -#include -#include -#include -#include - - -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/model/relocate.c b/src/model/relocate.c deleted file mode 100644 index 972bda3..0000000 --- a/src/model/relocate.c +++ /dev/null @@ -1,169 +0,0 @@ -#include -#include -#include -#include - - -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/model/section.c b/src/model/section.c deleted file mode 100644 index a96377c..0000000 --- a/src/model/section.c +++ /dev/null @@ -1,193 +0,0 @@ -#include -#include -#include - - -/* 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/model/toFile.c b/src/model/toFile.c deleted file mode 100644 index da06f4a..0000000 --- a/src/model/toFile.c +++ /dev/null @@ -1,115 +0,0 @@ -#include -#include - - - -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); -} 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 +#include +#include +#include + + +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 +#include +#include +#include + +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 +#include + + +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 +#include +#include +#include +#include + + +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 +#include +#include +#include + + + +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 +#include + + +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 +#include +#include +#include +#include + + +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 +#include +#include +#include + + +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 +#include +#include + + +/* 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 +#include + + + +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); +}