From: norly Date: Thu, 20 Jun 2013 00:56:24 +0000 (+0100) Subject: Separate library code, build .a/.so X-Git-Url: https://git.enpas.org/?p=centaur.git;a=commitdiff_plain;h=b70b3ff9b1679bb1e0a215b7acd9b6d55497a46b Separate library code, build .a/.so --- diff --git a/Makefile b/Makefile index c93b2ab..e063a29 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ -PROJ := elfucli +LIBNAME := elfu +EXENAME := elfucli LIBRARIES := libelf @@ -6,21 +7,31 @@ BUILDDIR := build INCLUDEDIR := include SRCDIR := src -EXE := $(BUILDDIR)/$(PROJ) +EXE := $(BUILDDIR)/$(EXENAME) +STATICLIB := $(BUILDDIR)/lib$(LIBNAME).a + +SHARED_VERMAJ := 0 +SHARED_VERMIN := 0 +SHARED_VERREV := 0 +SHAREDLIB := $(BUILDDIR)/lib$(LIBNAME).so.$(SHARED_VERMAJ).$(SHARED_VERMIN).$(SHARED_VERREV) + HEADERS := $(shell find $(INCLUDEDIR)/ -iname "*.h") HEADERS += $(shell find $(SRCDIR)/ -iname "*.h") -SOURCES := $(shell find $(SRCDIR)/ -iname "*.c") -OBJS := $(patsubst %.c, $(BUILDDIR)/%.o, $(SOURCES)) +ALLSRCS := $(shell find $(SRCDIR)/ -iname "*.c") +LIBSRCS := $(filter $(SRCDIR)/lib$(LIBNAME)/%.c, $(ALLSRCS)) +EXESRCS := $(filter-out $(SRCDIR)/lib$(LIBNAME)/%.c, $(ALLSRCS)) +LIBOBJS := $(patsubst %.c, $(BUILDDIR)/%.o, $(LIBSRCS)) +EXEOBJS := $(patsubst %.c, $(BUILDDIR)/%.o, $(EXESRCS)) INCLUDES := $(patsubst %, -I%, $(INCLUDEDIR) $(SRCDIR)) $(shell pkg-config --cflags-only-I $(LIBRARIES)) -CFLAGS := -g -Wall -pedantic -Wno-variadic-macros $(shell pkg-config --cflags-only-other $(LIBRARIES)) +CFLAGS := -g -Wall -pedantic -Wno-variadic-macros -fPIC $(shell pkg-config --cflags-only-other $(LIBRARIES)) LDFLAGS := $(shell pkg-config --libs $(LIBRARIES)) -.PHONY: default -default: $(EXE) +.PHONY: all +all: $(STATICLIB) $(SHAREDLIB) $(EXE) .PHONY: check @@ -30,13 +41,17 @@ check: $(EXE) .PHONY: debug debug: $(EXE) - gdb $(EXE) $(shell ps -e | sed "s/^ *\([0-9]\+\) .*$(PROJ).*$$/\1/g;te;d;:e") + gdb $(EXE) $(shell ps -e | sed "s/^ *\([0-9]\+\) .*$(EXENAME).*$$/\1/g;te;d;:e") -$(EXE): $(OBJS) - @if [ ! -d $(BUILDDIR) ] ; then echo "Error: Build dir '$(BUILDDIR)' does not exist." ; false ; fi +$(EXE): $(EXEOBJS) $(STATICLIB) gcc -o $@ $^ $(LDFLAGS) +$(SHAREDLIB): $(LIBOBJS) + gcc -shared -Wl,-soname,lib$(LIBNAME).so.$(SHARED_VERMAJ) -o $@ $^ $(LDFLAGS) + +$(STATICLIB): $(LIBOBJS) + ar rcs $@ $^ $(BUILDDIR)/$(SRCDIR)/%.o: $(SRCDIR)/%.c $(HEADERS) @if [ ! -d $(dir $@) ] ; then mkdir -p $(dir $@) ; fi @@ -45,9 +60,6 @@ $(BUILDDIR)/$(SRCDIR)/%.o: $(SRCDIR)/%.c $(HEADERS) .PHONY: clean clean: - rm -f $(STATICLIB) - rm -f $(OBJS) - rm -f $(TESTEXES) rm -rf $(BUILDDIR)/ make -C tests clean diff --git a/src/elfops/check.c b/src/elfops/check.c deleted file mode 100644 index e184b26..0000000 --- a/src/elfops/check.c +++ /dev/null @@ -1,202 +0,0 @@ -#include -#include -#include - -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/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 +#include +#include + +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 +#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/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 +#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; + + 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 +#include + +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 +#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 %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 +#include +#include +#include +#include + + +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 +#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%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 +#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/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 +#include +#include +#include +#include + + +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 +#include +#include +#include + + +/* 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 +#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) +{ + 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 +#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 *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 +#include +#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) +{ + 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); +} diff --git a/src/modelops/check.c b/src/modelops/check.c deleted file mode 100644 index 5234bef..0000000 --- a/src/modelops/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/modelops/clone.c b/src/modelops/clone.c deleted file mode 100644 index 8f92919..0000000 --- a/src/modelops/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; - - CIRCLEQ_INIT(&ms->symtab.syms); - CIRCLEQ_INIT(&ms->reltab.rels); - - return newscn; -} diff --git a/src/modelops/detour.c b/src/modelops/detour.c deleted file mode 100644 index 075d945..0000000 --- a/src/modelops/detour.c +++ /dev/null @@ -1,51 +0,0 @@ -#include -#include - -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/modelops/dump.c b/src/modelops/dump.c deleted file mode 100644 index b2e172e..0000000 --- a/src/modelops/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 %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/modelops/fromFile.c b/src/modelops/fromFile.c deleted file mode 100644 index dd8b462..0000000 --- a/src/modelops/fromFile.c +++ /dev/null @@ -1,490 +0,0 @@ -#include -#include -#include -#include -#include - - -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/modelops/layout.c b/src/modelops/layout.c deleted file mode 100644 index 8abc766..0000000 --- a/src/modelops/layout.c +++ /dev/null @@ -1,324 +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%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/modelops/phdr.c b/src/modelops/phdr.c deleted file mode 100644 index d26eb77..0000000 --- a/src/modelops/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/modelops/reladd.c b/src/modelops/reladd.c deleted file mode 100644 index bc909e4..0000000 --- a/src/modelops/reladd.c +++ /dev/null @@ -1,323 +0,0 @@ -#include -#include -#include -#include -#include - - -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/modelops/relocate.c b/src/modelops/relocate.c deleted file mode 100644 index eefed02..0000000 --- a/src/modelops/relocate.c +++ /dev/null @@ -1,70 +0,0 @@ -#include -#include -#include -#include - - -/* 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/modelops/section.c b/src/modelops/section.c deleted file mode 100644 index 2675126..0000000 --- a/src/modelops/section.c +++ /dev/null @@ -1,196 +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) -{ - 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/modelops/symtab.c b/src/modelops/symtab.c deleted file mode 100644 index ef8443f..0000000 --- a/src/modelops/symtab.c +++ /dev/null @@ -1,269 +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 *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/modelops/toFile.c b/src/modelops/toFile.c deleted file mode 100644 index 368f12a..0000000 --- a/src/modelops/toFile.c +++ /dev/null @@ -1,121 +0,0 @@ -#include -#include -#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) -{ - 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); -}