-PROJ := elfucli
+LIBNAME := elfu
+EXENAME := elfucli
LIBRARIES := libelf
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
.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
.PHONY: clean
clean:
- rm -f $(STATICLIB)
- rm -f $(OBJS)
- rm -f $(TESTEXES)
rm -rf $(BUILDDIR)/
make -C tests clean
+++ /dev/null
-#include <assert.h>
-#include <stdlib.h>
-#include <libelfu/libelfu.h>
-
-int elfu_eCheck(Elf *e)
-{
- int elfclass;
- GElf_Ehdr ehdr;
- GElf_Phdr *phdrs = NULL;
- GElf_Shdr *shdrs = NULL;
- size_t i, j, numPhdr, numShdr;
- int retval = 0;
-
- assert(e);
-
- elfclass = gelf_getclass(e);
- if (elfclass == ELFCLASSNONE) {
- ELFU_WARNELF("getclass");
- goto ERROR;
- }
-
- if (!gelf_getehdr(e, &ehdr)) {
- ELFU_WARNELF("gelf_getehdr");
- goto ERROR;
- }
-
- if (ehdr.e_machine != EM_386 && ehdr.e_machine != EM_X86_64) {
- ELFU_WARN("Sorry, only x86-32 and x86-64 ELF files are supported at the moment.\n");
- goto ERROR;
- }
-
- if (elf_getphdrnum(e, &numPhdr)) {
- ELFU_WARNELF("elf_getphdrnum");
- goto ERROR;
- }
-
- if (elf_getshdrnum(e, &numShdr)) {
- ELFU_WARNELF("elf_getshdrnum");
- goto ERROR;
- }
-
-
- if (numPhdr > 0) {
- phdrs = malloc(numPhdr * sizeof(GElf_Phdr));
- if (!phdrs) {
- ELFU_WARN("elfu_eCheck: malloc() failed for phdrs.\n");
- goto ERROR;
- }
-
- /* Attempt to load all PHDRs at once to catch any errors early */
- for (i = 0; i < numPhdr; i++) {
- GElf_Phdr phdr;
- if (gelf_getphdr(e, i, &phdr) != &phdr) {
- ELFU_WARN("gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1));
- goto ERROR;
- }
-
- phdrs[i] = phdr;
- }
-
- /* Check that LOAD PHDR memory ranges do not overlap, and that others
- * are either fully contained in a LOAD range, or not at all. */
- for (i = 0; i < numPhdr; i++) {
- if (phdrs[i].p_type != PT_LOAD) {
- continue;
- }
-
- for (j = 0; j < numPhdr; j++) {
- if (j == i || phdrs[j].p_type != PT_LOAD) {
- continue;
- }
-
- if (OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz,
- phdrs[j].p_vaddr, phdrs[j].p_memsz)) {
- if (phdrs[j].p_type == PT_LOAD) {
- ELFU_WARN("elfu_eCheck: Found LOAD PHDRs that overlap in memory.\n");
- goto ERROR;
- } else if (!FULLY_OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz,
- phdrs[j].p_vaddr, phdrs[j].p_memsz)) {
- ELFU_WARN("elfu_eCheck: PHDRs %d and %d partially overlap in memory.\n", i, j);
- goto ERROR;
- }
- }
- }
- }
- }
-
-
- if (numShdr > 1) {
- /* SHDRs should not overlap with PHDRs. */
- if (OVERLAPPING(ehdr.e_shoff, numShdr * ehdr.e_shentsize,
- ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) {
- ELFU_WARN("elfu_eCheck: SHDRs overlap with PHDRs.\n");
- goto ERROR;
- }
-
- shdrs = malloc(numShdr * sizeof(GElf_Shdr));
- if (!shdrs) {
- ELFU_WARN("elfu_eCheck: malloc() failed for shdrs.\n");
- goto ERROR;
- }
-
- /* Attempt to load all SHDRs at once to catch any errors early */
- for (i = 1; i < numShdr; i++) {
- Elf_Scn *scn;
- GElf_Shdr shdr;
-
- scn = elf_getscn(e, i);
- if (!scn) {
- ELFU_WARN("elf_getscn() failed for #%d: %s\n", i, elf_errmsg(-1));
- }
-
- if (gelf_getshdr(scn, &shdr) != &shdr) {
- ELFU_WARNELF("gelf_getshdr");
- goto ERROR;
- }
-
- shdrs[i] = shdr;
- }
-
-
- /* Check that Section memory ranges do not overlap.
- * NB: Section 0 is reserved and thus ignored. */
- for (i = 1; i < numShdr; i++) {
- /* Section should not overlap with EHDR. */
- if (shdrs[i].sh_offset == 0) {
- ELFU_WARN("elfu_eCheck: Section %d overlaps with EHDR.\n", i);
- goto ERROR;
- }
-
- /* Section should not overlap with PHDRs. */
- if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
- ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) {
- ELFU_WARN("elfu_eCheck: Section %d overlaps with PHDR.\n", i);
- goto ERROR;
- }
-
- /* Section should not overlap with SHDRs. */
- if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
- ehdr.e_shoff, numShdr * ehdr.e_shentsize)) {
- ELFU_WARN("elfu_eCheck: Section %d overlaps with SHDRs.\n", i);
- goto ERROR;
- }
-
- for (j = 1; j < numShdr; j++) {
- if (j == i) {
- continue;
- }
-
- /* Sections must not overlap in memory. */
- if (shdrs[i].sh_addr != 0
- && shdrs[j].sh_addr != 0
- && OVERLAPPING(shdrs[i].sh_addr, shdrs[i].sh_size,
- shdrs[j].sh_addr, shdrs[j].sh_size)) {
- ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in memory.\n", i, j);
- goto ERROR;
- }
-
- /* Sections must not overlap in file. */
- if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
- shdrs[j].sh_offset, SCNFILESIZE(&shdrs[j]))) {
- ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in file.\n", i, j);
- goto ERROR;
- }
- }
-
- /* Section addr/offset should match parent PHDR.
- * Find parent PHDR: */
- for (j = 0; j < numPhdr; j++) {
- if (PHDR_CONTAINS_SCN_IN_MEMORY(&phdrs[j], &shdrs[i])) {
- GElf_Off shoff = phdrs[j].p_offset + (shdrs[i].sh_addr - phdrs[j].p_vaddr);
-
- if (shdrs[i].sh_offset != shoff
- || !PHDR_CONTAINS_SCN_IN_FILE(&phdrs[j], &shdrs[i])) {
- ELFU_WARN("elfu_eCheck: Memory/file offsets/sizes are not congruent for SHDR %d, PHDR %d.\n", i, j);
- goto ERROR;
- }
- }
- }
-
- /* sh_link members should not point to sections out of range. */
- if (shdrs[i].sh_link >= numShdr) {
- ELFU_WARN("elfu_eCheck: Bogus sh_link in SHDR %d.\n", i);
- }
- }
- }
-
-
- DONE:
- if (phdrs) {
- free(phdrs);
- }
- if (shdrs) {
- free(shdrs);
- }
- return retval;
-
- ERROR:
- ELFU_WARN("elfu_eCheck: Errors found.\n");
- retval = -1;
- goto DONE;
-}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <libelfu/libelfu.h>
+
+int elfu_eCheck(Elf *e)
+{
+ int elfclass;
+ GElf_Ehdr ehdr;
+ GElf_Phdr *phdrs = NULL;
+ GElf_Shdr *shdrs = NULL;
+ size_t i, j, numPhdr, numShdr;
+ int retval = 0;
+
+ assert(e);
+
+ elfclass = gelf_getclass(e);
+ if (elfclass == ELFCLASSNONE) {
+ ELFU_WARNELF("getclass");
+ goto ERROR;
+ }
+
+ if (!gelf_getehdr(e, &ehdr)) {
+ ELFU_WARNELF("gelf_getehdr");
+ goto ERROR;
+ }
+
+ if (ehdr.e_machine != EM_386 && ehdr.e_machine != EM_X86_64) {
+ ELFU_WARN("Sorry, only x86-32 and x86-64 ELF files are supported at the moment.\n");
+ goto ERROR;
+ }
+
+ if (elf_getphdrnum(e, &numPhdr)) {
+ ELFU_WARNELF("elf_getphdrnum");
+ goto ERROR;
+ }
+
+ if (elf_getshdrnum(e, &numShdr)) {
+ ELFU_WARNELF("elf_getshdrnum");
+ goto ERROR;
+ }
+
+
+ if (numPhdr > 0) {
+ phdrs = malloc(numPhdr * sizeof(GElf_Phdr));
+ if (!phdrs) {
+ ELFU_WARN("elfu_eCheck: malloc() failed for phdrs.\n");
+ goto ERROR;
+ }
+
+ /* Attempt to load all PHDRs at once to catch any errors early */
+ for (i = 0; i < numPhdr; i++) {
+ GElf_Phdr phdr;
+ if (gelf_getphdr(e, i, &phdr) != &phdr) {
+ ELFU_WARN("gelf_getphdr() failed for #%d: %s\n", i, elf_errmsg(-1));
+ goto ERROR;
+ }
+
+ phdrs[i] = phdr;
+ }
+
+ /* Check that LOAD PHDR memory ranges do not overlap, and that others
+ * are either fully contained in a LOAD range, or not at all. */
+ for (i = 0; i < numPhdr; i++) {
+ if (phdrs[i].p_type != PT_LOAD) {
+ continue;
+ }
+
+ for (j = 0; j < numPhdr; j++) {
+ if (j == i || phdrs[j].p_type != PT_LOAD) {
+ continue;
+ }
+
+ if (OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz,
+ phdrs[j].p_vaddr, phdrs[j].p_memsz)) {
+ if (phdrs[j].p_type == PT_LOAD) {
+ ELFU_WARN("elfu_eCheck: Found LOAD PHDRs that overlap in memory.\n");
+ goto ERROR;
+ } else if (!FULLY_OVERLAPPING(phdrs[i].p_vaddr, phdrs[i].p_memsz,
+ phdrs[j].p_vaddr, phdrs[j].p_memsz)) {
+ ELFU_WARN("elfu_eCheck: PHDRs %d and %d partially overlap in memory.\n", i, j);
+ goto ERROR;
+ }
+ }
+ }
+ }
+ }
+
+
+ if (numShdr > 1) {
+ /* SHDRs should not overlap with PHDRs. */
+ if (OVERLAPPING(ehdr.e_shoff, numShdr * ehdr.e_shentsize,
+ ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) {
+ ELFU_WARN("elfu_eCheck: SHDRs overlap with PHDRs.\n");
+ goto ERROR;
+ }
+
+ shdrs = malloc(numShdr * sizeof(GElf_Shdr));
+ if (!shdrs) {
+ ELFU_WARN("elfu_eCheck: malloc() failed for shdrs.\n");
+ goto ERROR;
+ }
+
+ /* Attempt to load all SHDRs at once to catch any errors early */
+ for (i = 1; i < numShdr; i++) {
+ Elf_Scn *scn;
+ GElf_Shdr shdr;
+
+ scn = elf_getscn(e, i);
+ if (!scn) {
+ ELFU_WARN("elf_getscn() failed for #%d: %s\n", i, elf_errmsg(-1));
+ }
+
+ if (gelf_getshdr(scn, &shdr) != &shdr) {
+ ELFU_WARNELF("gelf_getshdr");
+ goto ERROR;
+ }
+
+ shdrs[i] = shdr;
+ }
+
+
+ /* Check that Section memory ranges do not overlap.
+ * NB: Section 0 is reserved and thus ignored. */
+ for (i = 1; i < numShdr; i++) {
+ /* Section should not overlap with EHDR. */
+ if (shdrs[i].sh_offset == 0) {
+ ELFU_WARN("elfu_eCheck: Section %d overlaps with EHDR.\n", i);
+ goto ERROR;
+ }
+
+ /* Section should not overlap with PHDRs. */
+ if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
+ ehdr.e_phoff, numPhdr * ehdr.e_phentsize)) {
+ ELFU_WARN("elfu_eCheck: Section %d overlaps with PHDR.\n", i);
+ goto ERROR;
+ }
+
+ /* Section should not overlap with SHDRs. */
+ if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
+ ehdr.e_shoff, numShdr * ehdr.e_shentsize)) {
+ ELFU_WARN("elfu_eCheck: Section %d overlaps with SHDRs.\n", i);
+ goto ERROR;
+ }
+
+ for (j = 1; j < numShdr; j++) {
+ if (j == i) {
+ continue;
+ }
+
+ /* Sections must not overlap in memory. */
+ if (shdrs[i].sh_addr != 0
+ && shdrs[j].sh_addr != 0
+ && OVERLAPPING(shdrs[i].sh_addr, shdrs[i].sh_size,
+ shdrs[j].sh_addr, shdrs[j].sh_size)) {
+ ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in memory.\n", i, j);
+ goto ERROR;
+ }
+
+ /* Sections must not overlap in file. */
+ if (OVERLAPPING(shdrs[i].sh_offset, SCNFILESIZE(&shdrs[i]),
+ shdrs[j].sh_offset, SCNFILESIZE(&shdrs[j]))) {
+ ELFU_WARN("elfu_eCheck: Sections %d and %d overlap in file.\n", i, j);
+ goto ERROR;
+ }
+ }
+
+ /* Section addr/offset should match parent PHDR.
+ * Find parent PHDR: */
+ for (j = 0; j < numPhdr; j++) {
+ if (PHDR_CONTAINS_SCN_IN_MEMORY(&phdrs[j], &shdrs[i])) {
+ GElf_Off shoff = phdrs[j].p_offset + (shdrs[i].sh_addr - phdrs[j].p_vaddr);
+
+ if (shdrs[i].sh_offset != shoff
+ || !PHDR_CONTAINS_SCN_IN_FILE(&phdrs[j], &shdrs[i])) {
+ ELFU_WARN("elfu_eCheck: Memory/file offsets/sizes are not congruent for SHDR %d, PHDR %d.\n", i, j);
+ goto ERROR;
+ }
+ }
+ }
+
+ /* sh_link members should not point to sections out of range. */
+ if (shdrs[i].sh_link >= numShdr) {
+ ELFU_WARN("elfu_eCheck: Bogus sh_link in SHDR %d.\n", i);
+ }
+ }
+ }
+
+
+ DONE:
+ if (phdrs) {
+ free(phdrs);
+ }
+ if (shdrs) {
+ free(shdrs);
+ }
+ return retval;
+
+ ERROR:
+ ELFU_WARN("elfu_eCheck: Errors found.\n");
+ retval = -1;
+ goto DONE;
+}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <libelfu/libelfu.h>
+
+
+int elfu_mCheck(ElfuElf *me)
+{
+ size_t numSecs;
+ ElfuScn **sortedSecs;
+ size_t i;
+
+ sortedSecs = elfu_mScnSortedByOffset(me, &numSecs);
+ if (!sortedSecs) {
+ return -1;
+ }
+
+
+ /* Check for overlapping sections */
+ for (i = 0; i < numSecs - 1; i++) {
+ if (sortedSecs[i]->shdr.sh_offset + SCNFILESIZE(&sortedSecs[i]->shdr)
+ > sortedSecs[i+1]->shdr.sh_offset) {
+ ELFU_WARN("elfu_check: Found overlapping sections: %s and %s.\n",
+ elfu_mScnName(me, sortedSecs[i]),
+ elfu_mScnName(me, sortedSecs[i+1]));
+ }
+ }
+
+
+ /* Check for sections overlapping with EHDR */
+ for (i = 0; i < numSecs; i++) {
+ if (sortedSecs[i]->shdr.sh_offset < me->ehdr.e_ehsize) {
+ ELFU_WARN("elfu_check: Found section overlapping with EHDR: %s.\n",
+ elfu_mScnName(me, sortedSecs[i]));
+ }
+ }
+
+
+ /* Check for sections overlapping with PHDRs */
+ for (i = 0; i < numSecs; i++) {
+ if (OVERLAPPING(sortedSecs[i]->shdr.sh_offset,
+ SCNFILESIZE(&sortedSecs[i]->shdr),
+ me->ehdr.e_phoff,
+ me->ehdr.e_phentsize * me->ehdr.e_phnum)) {
+ ELFU_WARN("elfu_check: Found section overlapping with PHDRs: %s.\n",
+ elfu_mScnName(me, sortedSecs[i]));
+ }
+ }
+
+
+ free(sortedSecs);
+
+ return 0;
+}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libelfu/libelfu.h>
+
+ElfuScn* elfu_mCloneScn(ElfuScn *ms)
+{
+ ElfuScn *newscn;
+
+ assert(ms);
+
+ newscn = malloc(sizeof(ElfuScn));
+ if (!newscn) {
+ ELFU_WARN("elfu_nCloneScn: Could not allocate memory for new ElfuScn.\n");
+ return NULL;
+ }
+
+ newscn->shdr = ms->shdr;
+ newscn->data = ms->data;
+ if (ms->data.d_buf) {
+ void *newbuf = malloc(ms->data.d_size);
+ if (!newbuf) {
+ ELFU_WARN("elfu_nCloneScn: Could not allocate memory for new data buffer.\n");
+ free(newscn);
+ return NULL;
+ }
+
+ memcpy(newbuf, ms->data.d_buf, ms->data.d_size);
+ newscn->data.d_buf = newbuf;
+ }
+
+ newscn->linkptr = NULL;
+ newscn->infoptr = NULL;
+
+ newscn->oldptr = ms;
+
+ CIRCLEQ_INIT(&ms->symtab.syms);
+ CIRCLEQ_INIT(&ms->reltab.rels);
+
+ return newscn;
+}
--- /dev/null
+#include <libelfu/libelfu.h>
+#include <string.h>
+
+static void* subFindByAddr(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
+{
+ GElf_Addr a = *(GElf_Addr*)aux1;
+
+ if (OVERLAPPING(ms->shdr.sh_addr, ms->shdr.sh_size, a, 1)) {
+ return ms;
+ }
+
+ /* Continue */
+ return NULL;
+}
+
+
+void elfu_mDetour(ElfuElf *me, GElf_Addr from, GElf_Addr to)
+{
+ ElfuScn *ms;
+ GElf_Word scnoffset;
+ unsigned char detourcode[] = {0xe9, 0xfc, 0xff, 0xff, 0xff};
+
+ ms = elfu_mScnForall(me, subFindByAddr, &from, NULL);
+
+ if (!ms) {
+ ELFU_WARN("mDetour: Cannot find address %x in any section.\n",
+ (unsigned)from);
+ return;
+ }
+
+ if (ms->shdr.sh_type != SHT_PROGBITS) {
+ ELFU_WARN("mDetour: Cannot detour in non-PROGBITS section %s.\n",
+ elfu_mScnName(me, ms));
+ return;
+ }
+
+ scnoffset = from - ms->shdr.sh_addr;
+
+ if (ms->shdr.sh_size - scnoffset < 5) {
+ ELFU_WARN("mDetour: Not enough space to insert a detour.\n");
+ return;
+ }
+
+ ELFU_DEBUG("mDetour: Detouring at address %x in section %s to %x.\n",
+ (unsigned)from,
+ elfu_mScnName(me, ms),
+ (unsigned)to);
+
+ *(Elf32_Word*)(detourcode + 1) = to - from - 5;
+ memcpy((char*)ms->data.d_buf + scnoffset, detourcode, 5);
+}
--- /dev/null
+#include <assert.h>
+#include <libelfu/libelfu.h>
+
+
+static char* segmentTypeStr(Elf32_Word p_type)
+{
+ switch(p_type) {
+ case PT_NULL: /* 0 */
+ return "NULL";
+ case PT_LOAD: /* 1 */
+ return "LOAD";
+ case PT_DYNAMIC: /* 2 */
+ return "DYNAMIC";
+ case PT_INTERP: /* 3 */
+ return "INTERP";
+ case PT_NOTE: /* 4 */
+ return "NOTE";
+ case PT_SHLIB: /* 5 */
+ return "SHLIB";
+ case PT_PHDR: /* 6 */
+ return "PHDR";
+ case PT_TLS: /* 7 */
+ return "TLS";
+ case PT_NUM: /* 8 */
+ return "NUM";
+ case PT_GNU_EH_FRAME: /* 0x6474e550 */
+ return "GNU_EH_FRAME";
+ case PT_GNU_STACK: /* 0x6474e551 */
+ return "GNU_STACK";
+ case PT_GNU_RELRO: /* 0x6474e552 */
+ return "GNU_RELRO";
+ }
+
+ return "-?-";
+}
+
+static char* sectionTypeStr(Elf32_Word sh_type)
+{
+ switch(sh_type) {
+ case SHT_NULL: /* 0 */
+ return "NULL";
+ case SHT_PROGBITS: /* 1 */
+ return "PROGBITS";
+ case SHT_SYMTAB: /* 2 */
+ return "SYMTAB";
+ case SHT_STRTAB: /* 3 */
+ return "STRTAB";
+ case SHT_RELA: /* 4 */
+ return "RELA";
+ case SHT_HASH: /* 5 */
+ return "HASH";
+ case SHT_DYNAMIC: /* 6 */
+ return "DYNAMIC";
+ case SHT_NOTE: /* 7 */
+ return "NOTE";
+ case SHT_NOBITS: /* 8 */
+ return "NOBITS";
+ case SHT_REL: /* 9 */
+ return "REL";
+ case SHT_SHLIB: /* 10 */
+ return "SHLIB";
+ case SHT_DYNSYM: /* 11 */
+ return "DYNSYM";
+ case SHT_INIT_ARRAY: /* 14 */
+ return "INIT_ARRAY";
+ case SHT_FINI_ARRAY: /* 15 */
+ return "FINI_ARRAY";
+ case SHT_PREINIT_ARRAY: /* 16 */
+ return "PREINIT_ARRAY";
+ case SHT_GROUP: /* 17 */
+ return "SHT_GROUP";
+ case SHT_SYMTAB_SHNDX: /* 18 */
+ return "SYMTAB_SHNDX";
+ case SHT_NUM: /* 19 */
+ return "NUM";
+
+ case SHT_GNU_ATTRIBUTES: /* 0x6ffffff5 */
+ return "GNU_ATTRIBUTES";
+ case SHT_GNU_HASH: /* 0x6ffffff6 */
+ return "GNU_HASH";
+ case SHT_GNU_LIBLIST: /* 0x6ffffff7 */
+ return "GNU_LIBLIST";
+ case SHT_GNU_verdef: /* 0x6ffffffd */
+ return "GNU_verdef";
+ case SHT_GNU_verneed: /* 0x6ffffffe */
+ return "GNU_verneed";
+ case SHT_GNU_versym: /* 0x6fffffff */
+ return "GNU_versym";
+ }
+
+ return "-?-";
+}
+
+
+
+
+void elfu_mDumpPhdr(ElfuElf *me, ElfuPhdr *mp)
+{
+ assert(me);
+ assert(mp);
+
+ ELFU_INFO("%12s %8x %8x %8x %8x %8x %8x %8x %8x\n",
+ segmentTypeStr(mp->phdr.p_type),
+ (unsigned)mp->phdr.p_type,
+ (unsigned)mp->phdr.p_offset,
+ (unsigned)mp->phdr.p_vaddr,
+ (unsigned)mp->phdr.p_paddr,
+ (unsigned)mp->phdr.p_filesz,
+ (unsigned)mp->phdr.p_memsz,
+ (unsigned)mp->phdr.p_flags,
+ (unsigned)mp->phdr.p_align);
+
+ if (!CIRCLEQ_EMPTY(&mp->childPhdrList)) {
+ ElfuPhdr *mpc;
+
+ ELFU_INFO(" -> Child PHDRs:\n");
+ CIRCLEQ_FOREACH(mpc, &mp->childPhdrList, elemChildPhdr) {
+ ELFU_INFO(" * %-8s @ %8x\n",
+ segmentTypeStr(mpc->phdr.p_type),
+ (unsigned)mpc->phdr.p_vaddr);
+ }
+ }
+
+ if (!CIRCLEQ_EMPTY(&mp->childScnList)) {
+ ElfuScn *msc;
+
+ ELFU_INFO(" -> Child sections:\n");
+ CIRCLEQ_FOREACH(msc, &mp->childScnList, elemChildScn) {
+ ELFU_INFO(" * %-17s @ %8x\n",
+ elfu_mScnName(me, msc),
+ (unsigned)msc->shdr.sh_addr);
+ }
+ }
+}
+
+
+void elfu_mDumpScn(ElfuElf *me, ElfuScn *ms)
+{
+ char *namestr, *typestr, *linkstr, *infostr;
+
+ assert(me);
+ assert(ms);
+
+ namestr = elfu_mScnName(me, ms);
+ typestr = sectionTypeStr(ms->shdr.sh_type);
+ linkstr = ms->linkptr ? elfu_mScnName(me, ms->linkptr) : "";
+ infostr = ms->infoptr ? elfu_mScnName(me, ms->infoptr) : "";
+
+ ELFU_INFO("%-17s %-15s %8x %9x %8x %2x %2x %2d %-17s %-17s\n",
+ namestr,
+ typestr,
+ (unsigned)ms->shdr.sh_addr,
+ (unsigned)ms->shdr.sh_offset,
+ (unsigned)ms->shdr.sh_size,
+ (unsigned)ms->shdr.sh_entsize,
+ (unsigned)ms->shdr.sh_flags,
+ (unsigned)ms->shdr.sh_addralign,
+ linkstr,
+ infostr);
+}
+
+
+void elfu_mDumpEhdr(ElfuElf *me)
+{
+ assert(me);
+
+ ELFU_INFO("ELF header:\n");
+ ELFU_INFO(" %d-bit ELF object\n", me->elfclass == ELFCLASS32 ? 32 : 64);
+
+ ELFU_INFO(" EHDR:\n");
+
+ ELFU_INFO(" e_type %8x\n", me->ehdr.e_type);
+ ELFU_INFO(" e_machine %8x\n", me->ehdr.e_machine);
+ ELFU_INFO(" e_version %8x\n", me->ehdr.e_version);
+ ELFU_INFO(" e_entry %8x\n", (unsigned)me->ehdr.e_entry);
+ ELFU_INFO(" e_phoff %8x\n", (unsigned)me->ehdr.e_phoff);
+ ELFU_INFO(" e_shoff %8x\n", (unsigned)me->ehdr.e_shoff);
+ ELFU_INFO(" e_flags %8x\n", me->ehdr.e_flags);
+ ELFU_INFO(" e_ehsize %8x\n", me->ehdr.e_ehsize);
+ ELFU_INFO(" e_phentsize %8x\n", me->ehdr.e_phentsize);
+ ELFU_INFO(" e_shentsize %8x\n", me->ehdr.e_shentsize);
+
+ ELFU_INFO(" shstrtab: %s\n", me->shstrtab ? elfu_mScnName(me, me->shstrtab) : "(none)");
+}
+
+
+
+static void* subScnDump(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
+{
+ (void) aux1;
+ (void) aux2;
+
+ printf(" [%4d] ", elfu_mScnIndex(me, ms));
+ elfu_mDumpScn(me, ms);
+
+ return NULL;
+}
+
+
+void elfu_mDumpElf(ElfuElf *me)
+{
+ ElfuPhdr *mp;
+ ElfuScn *ms;
+ size_t i;
+
+ assert(me);
+
+
+ elfu_mDumpEhdr(me);
+ ELFU_INFO("\n");
+
+
+ ELFU_INFO("Segments:\n");
+ ELFU_INFO(" # (type) p_type p_offset p_vaddr p_paddr p_filesz p_memsz p_flags p_align\n");
+ ELFU_INFO(" | | | | | | | | | \n");
+ i = 0;
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ printf(" [%4d] ", i);
+ elfu_mDumpPhdr(me, mp);
+ i++;
+ }
+ ELFU_INFO("\n");
+
+
+ ELFU_INFO("Orphaned sections:\n");
+ CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
+ ELFU_INFO(" * %-17s @ %8x\n",
+ elfu_mScnName(me, ms),
+ (unsigned)ms->shdr.sh_addr);
+ }
+ ELFU_INFO("\n");
+
+
+ ELFU_INFO("Sections:\n");
+ ELFU_INFO(" # Name sh_type sh_addr sh_offset sh_size ES Fl Al sh_link sh_info \n");
+ ELFU_INFO(" | | | | | | | | | | \n");
+ elfu_mScnForall(me, subScnDump, &i, NULL);
+ ELFU_INFO("\n");
+}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <libelfu/libelfu.h>
+
+
+static void parseSymtab(ElfuElf *me, ElfuScn *ms, ElfuScn**origScnArr)
+{
+ ElfuSym *sym;
+ size_t i;
+
+ assert(ms);
+ assert(ms->data.d_buf);
+ assert(origScnArr);
+
+ /* Parse symbols from their elfclass-specific format */
+ if (me->elfclass == ELFCLASS32) {
+ for (i = 1; (i + 1) * sizeof(Elf32_Sym) <= ms->shdr.sh_size; i++) {
+ Elf32_Sym *cursym = ((Elf32_Sym*)ms->data.d_buf) + i;
+ ElfuSym *newsym = malloc(sizeof(*sym));
+ assert(newsym);
+
+ newsym->name = cursym->st_name;
+ newsym->value = cursym->st_value;
+ newsym->size = cursym->st_size;
+ newsym->bind = ELF32_ST_BIND(cursym->st_info);
+ newsym->type = ELF32_ST_TYPE(cursym->st_info);
+ newsym->other = cursym->st_other;
+ newsym->shndx = cursym->st_shndx;
+
+
+
+ CIRCLEQ_INSERT_TAIL(&ms->symtab.syms, newsym, elem);
+ }
+ } else if (me->elfclass == ELFCLASS64) {
+ for (i = 1; (i + 1) * sizeof(Elf64_Sym) <= ms->shdr.sh_size; i++) {
+ Elf64_Sym *cursym = ((Elf64_Sym*)ms->data.d_buf) + i;
+ ElfuSym *newsym = malloc(sizeof(*sym));
+ assert(newsym);
+
+ newsym->name = cursym->st_name;
+ newsym->value = cursym->st_value;
+ newsym->size = cursym->st_size;
+ newsym->bind = ELF64_ST_BIND(cursym->st_info);
+ newsym->type = ELF64_ST_TYPE(cursym->st_info);
+ newsym->other = cursym->st_other;
+ newsym->shndx = cursym->st_shndx;
+
+
+
+ CIRCLEQ_INSERT_TAIL(&ms->symtab.syms, newsym, elem);
+ }
+ } else {
+ /* Unknown elfclass */
+ assert(0);
+ }
+
+ /* For each section, find the section it points to if any. */
+ CIRCLEQ_FOREACH(sym, &ms->symtab.syms, elem) {
+ switch (sym->shndx) {
+ case SHN_UNDEF:
+ case SHN_ABS:
+ case SHN_COMMON:
+ sym->scnptr = NULL;
+ break;
+ default:
+ sym->scnptr = origScnArr[sym->shndx - 1];
+ break;
+ }
+ }
+}
+
+
+static void parseReltab32(ElfuScn *ms)
+{
+ size_t i;
+
+ assert(ms);
+ assert(ms->data.d_buf);
+
+
+ for (i = 0; (i + 1) * sizeof(Elf32_Rel) <= ms->shdr.sh_size; i++) {
+ Elf32_Rel *currel = &(((Elf32_Rel*)ms->data.d_buf)[i]);
+ ElfuRel *rel;
+
+ rel = malloc(sizeof(*rel));
+ assert(rel);
+
+ rel->offset = currel->r_offset;
+ rel->sym = ELF32_R_SYM(currel->r_info);
+ rel->type = ELF32_R_TYPE(currel->r_info);
+ rel->addendUsed = 0;
+ rel->addend = 0;
+
+ CIRCLEQ_INSERT_TAIL(&ms->reltab.rels, rel, elem);
+ }
+}
+
+
+static void parseRelatab64(ElfuScn *ms)
+{
+ size_t i;
+
+ assert(ms);
+ assert(ms->data.d_buf);
+
+
+ for (i = 0; (i + 1) * sizeof(Elf64_Rela) <= ms->shdr.sh_size; i++) {
+ Elf64_Rela *currel = &(((Elf64_Rela*)ms->data.d_buf)[i]);
+ ElfuRel *rel;
+
+ rel = malloc(sizeof(*rel));
+ assert(rel);
+
+ rel->offset = currel->r_offset;
+ rel->sym = ELF64_R_SYM(currel->r_info);
+ rel->type = ELF64_R_TYPE(currel->r_info);
+ rel->addendUsed = 1;
+ rel->addend = currel->r_addend;
+
+ CIRCLEQ_INSERT_TAIL(&ms->reltab.rels, rel, elem);
+ }
+}
+
+
+static int cmpScnOffs(const void *ms1, const void *ms2)
+{
+ ElfuScn *s1 = *(ElfuScn**)ms1;
+ ElfuScn *s2 = *(ElfuScn**)ms2;
+
+ assert(ms1);
+ assert(ms2);
+
+ s1 = *(ElfuScn**)ms1;
+ s2 = *(ElfuScn**)ms2;
+
+ assert(s1);
+ assert(s2);
+
+
+ if (s1->shdr.sh_offset < s2->shdr.sh_offset) {
+ return -1;
+ } else if (s1->shdr.sh_offset == s2->shdr.sh_offset) {
+ return 0;
+ } else /* if (s1->shdr.sh_offset > s2->shdr.sh_offset) */ {
+ return 1;
+ }
+}
+
+
+
+static ElfuPhdr* parentPhdr(ElfuElf *me, ElfuScn *ms)
+{
+ ElfuPhdr *mp;
+
+ assert(me);
+ assert(ms);
+
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ if (mp->phdr.p_type != PT_LOAD) {
+ continue;
+ }
+
+ if (PHDR_CONTAINS_SCN_IN_MEMORY(&mp->phdr, &ms->shdr)) {
+ return mp;
+ }
+
+ /* Give sections a second chance if they do not have any sh_addr
+ * at all. */
+ /* Actually we don't, because it's ambiguous.
+ * Re-enable for experiments with strangely-formatted files.
+ if (ms->shdr.sh_addr == 0
+ && PHDR_CONTAINS_SCN_IN_FILE(&mp->phdr, &ms->shdr)
+ && OFFS_END(ms->shdr.sh_offset, ms->shdr.sh_size)
+ <= OFFS_END(mp->phdr.p_offset, mp->phdr.p_memsz)) {
+ return mp;
+ }
+ */
+ }
+
+ return NULL;
+}
+
+
+static ElfuPhdr* modelFromPhdr(GElf_Phdr *phdr)
+{
+ ElfuPhdr *mp;
+
+ assert(phdr);
+
+ mp = malloc(sizeof(ElfuPhdr));
+ if (!mp) {
+ ELFU_WARN("modelFromPhdr: malloc() failed for ElfuPhdr.\n");
+ return NULL;
+ }
+
+ mp->phdr = *phdr;
+
+ CIRCLEQ_INIT(&mp->childScnList);
+ CIRCLEQ_INIT(&mp->childPhdrList);
+
+ return mp;
+}
+
+
+static ElfuScn* modelFromSection(Elf_Scn *scn)
+{
+ ElfuScn *ms;
+
+ assert(scn);
+
+ ms = malloc(sizeof(ElfuScn));
+ if (!ms) {
+ ELFU_WARN("modelFromSection: malloc() failed for ElfuScn.\n");
+ goto ERROR;
+ }
+
+
+ assert(gelf_getshdr(scn, &ms->shdr) == &ms->shdr);
+
+
+ /* Copy each data part in source segment */
+ ms->data.d_align = 1;
+ ms->data.d_buf = NULL;
+ ms->data.d_off = 0;
+ ms->data.d_type = ELF_T_BYTE;
+ ms->data.d_size = ms->shdr.sh_size;
+ ms->data.d_version = elf_version(EV_NONE);
+ if (ms->shdr.sh_type != SHT_NOBITS
+ && ms->shdr.sh_size > 0) {
+ Elf_Data *data;
+
+ ms->data.d_buf = malloc(ms->shdr.sh_size);
+ if (!ms->data.d_buf) {
+ ELFU_WARN("modelFromSection: malloc() failed for data buffer (%x bytes).\n", (unsigned)ms->shdr.sh_size);
+ goto ERROR;
+ }
+
+ /* A non-empty section should contain at least one data block. */
+ data = elf_rawdata(scn, NULL);
+ assert(data);
+
+ ms->data.d_align = data->d_align;
+ ms->data.d_type = data->d_type;
+ ms->data.d_version = data->d_version;
+
+ while (data) {
+ if (data->d_off + data->d_size > ms->shdr.sh_size) {
+ ELFU_WARN("modelFromSection: libelf delivered a bogus data blob. Skipping\n");
+ } else {
+ memcpy((char*)ms->data.d_buf + data->d_off, data->d_buf, data->d_size);
+ }
+
+ data = elf_rawdata(scn, data);
+ }
+ }
+
+ ms->linkptr = NULL;
+ ms->infoptr = NULL;
+
+ ms->oldptr = NULL;
+
+ CIRCLEQ_INIT(&ms->symtab.syms);
+ CIRCLEQ_INIT(&ms->reltab.rels);
+
+
+ return ms;
+
+ ERROR:
+ if (ms) {
+ free(ms);
+ }
+ return NULL;
+}
+
+
+
+
+ElfuElf* elfu_mFromElf(Elf *e)
+{
+ ElfuElf *me;
+ size_t shstrndx;
+ size_t i, numPhdr, numShdr;
+ ElfuScn **secArray = NULL;
+
+ assert(e);
+ if (elfu_eCheck(e)) {
+ goto ERROR;
+ }
+
+ me = malloc(sizeof(ElfuElf));
+ if (!me) {
+ ELFU_WARN("elfu_mFromElf: malloc() failed for ElfuElf.\n");
+ goto ERROR;
+ }
+
+
+ /* General stuff */
+ CIRCLEQ_INIT(&me->phdrList);
+ CIRCLEQ_INIT(&me->orphanScnList);
+ me->shstrtab = NULL;
+ me->symtab = NULL;
+
+ me->elfclass = gelf_getclass(e);
+ assert(me->elfclass != ELFCLASSNONE);
+ assert(gelf_getehdr(e, &me->ehdr) == &me->ehdr);
+
+
+ /* Get the section string table index */
+ if (elf_getshdrstrndx(e, &shstrndx) != 0) {
+ shstrndx = 0;
+ }
+
+
+ /* Load segments */
+ assert(!elf_getphdrnum(e, &numPhdr));
+ for (i = 0; i < numPhdr; i++) {
+ GElf_Phdr phdr;
+ ElfuPhdr *mp;
+
+ assert(gelf_getphdr(e, i, &phdr) == &phdr);
+
+ mp = modelFromPhdr(&phdr);
+ if (!mp) {
+ goto ERROR;
+ }
+
+ CIRCLEQ_INSERT_TAIL(&me->phdrList, mp, elem);
+ }
+
+ if (numPhdr > 0) {
+ ElfuPhdr *mp;
+
+ /* Find PHDR -> PHDR dependencies (needs sorted sections) */
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ ElfuPhdr *mp2;
+
+ if (mp->phdr.p_type != PT_LOAD) {
+ continue;
+ }
+
+ CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
+ if (mp2 == mp) {
+ continue;
+ }
+
+ if (mp->phdr.p_vaddr <= mp2->phdr.p_vaddr
+ && OFFS_END(mp2->phdr.p_vaddr, mp2->phdr.p_memsz) <= OFFS_END(mp->phdr.p_vaddr, mp->phdr.p_memsz)) {
+ CIRCLEQ_INSERT_TAIL(&mp->childPhdrList, mp2, elemChildPhdr);
+ }
+ }
+ }
+ }
+
+
+ /* Load sections */
+ assert(!elf_getshdrnum(e, &numShdr));
+ if (numShdr > 1) {
+ secArray = malloc((numShdr - 1) * sizeof(*secArray));
+ if (!secArray) {
+ ELFU_WARN("elfu_mFromElf: malloc() failed for secArray.\n");
+ goto ERROR;
+ }
+
+ for (i = 1; i < numShdr; i++) {
+ Elf_Scn *scn;
+ ElfuScn *ms;
+
+ scn = elf_getscn(e, i);
+ assert(scn);
+
+ ms = modelFromSection(scn);
+ if (!ms) {
+ goto ERROR;
+ }
+
+ secArray[i-1] = ms;
+
+ if (i == shstrndx) {
+ me->shstrtab = ms;
+ }
+ }
+
+
+ /* Find sh_link and sh_info dependencies (needs sections in original order) */
+ for (i = 0; i < numShdr - 1; i++) {
+ ElfuScn *ms = secArray[i];
+
+ switch (ms->shdr.sh_type) {
+ case SHT_REL:
+ case SHT_RELA:
+ if (ms->shdr.sh_info > 0) {
+ ms->infoptr = secArray[ms->shdr.sh_info - 1];
+ }
+ case SHT_DYNAMIC:
+ case SHT_HASH:
+ case SHT_SYMTAB:
+ case SHT_DYNSYM:
+ case SHT_GNU_versym:
+ case SHT_GNU_verdef:
+ case SHT_GNU_verneed:
+ if (ms->shdr.sh_link > 0) {
+ ms->linkptr = secArray[ms->shdr.sh_link - 1];
+ }
+ }
+ }
+
+
+ /* Parse symtabs (needs sections in original order) */
+ for (i = 0; i < numShdr - 1; i++) {
+ ElfuScn *ms = secArray[i];
+
+ switch (ms->shdr.sh_type) {
+ case SHT_SYMTAB:
+ me->symtab = ms;
+ case SHT_DYNSYM:
+ parseSymtab(me, ms, secArray);
+ break;
+ }
+ }
+
+
+ /* Parse relocations */
+ for (i = 0; i < numShdr - 1; i++) {
+ ElfuScn *ms = secArray[i];
+
+ switch (ms->shdr.sh_type) {
+ case SHT_REL:
+ if (me->elfclass == ELFCLASS32) {
+ parseReltab32(ms);
+ } else if (me->elfclass == ELFCLASS64) {
+ /* Not used on x86-64 */
+ assert(0);
+ }
+ break;
+ case SHT_RELA:
+ if (me->elfclass == ELFCLASS32) {
+ /* Not used on x86-32 */
+ assert(0);
+ } else if (me->elfclass == ELFCLASS64) {
+ parseRelatab64(ms);
+ }
+ break;
+ }
+ }
+
+
+ /* Sort sections by file offset */
+ qsort(secArray, numShdr - 1, sizeof(*secArray), cmpScnOffs);
+
+
+ /* Find PHDR -> Section dependencies (needs sorted sections) */
+ for (i = 0; i < numShdr - 1; i++) {
+ ElfuScn *ms = secArray[i];
+
+ ElfuPhdr *parent = parentPhdr(me, ms);
+
+ if (parent) {
+ GElf_Off shaddr = parent->phdr.p_vaddr +
+ (ms->shdr.sh_offset - parent->phdr.p_offset);
+
+ if (ms->shdr.sh_addr == 0) {
+ ms->shdr.sh_addr = shaddr;
+ } else {
+ assert(ms->shdr.sh_addr == shaddr);
+ }
+
+ CIRCLEQ_INSERT_TAIL(&parent->childScnList, ms, elemChildScn);
+ } else {
+ CIRCLEQ_INSERT_TAIL(&me->orphanScnList, ms, elemChildScn);
+ }
+ }
+ }
+
+
+ return me;
+
+
+ ERROR:
+ if (secArray) {
+ free(secArray);
+ }
+ if (me) {
+ // TODO: Free data structures
+ }
+
+ ELFU_WARN("elfu_mFromElf: Failed to load file.\n");
+ return NULL;
+}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libelfu/libelfu.h>
+
+
+
+static GElf_Word shiftStuffAtAfterOffset(ElfuElf *me,
+ GElf_Off offset,
+ GElf_Word size)
+{
+ ElfuPhdr *mp;
+ ElfuScn *ms;
+ /* Force a minimum alignment, just to be sure. */
+ GElf_Word align = 64;
+
+ /* Find maximum alignment size by which we have to shift.
+ * Assumes alignment sizes are always 2^x. */
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ if (mp->phdr.p_offset >= offset) {
+ if (mp->phdr.p_align > align) {
+ align = mp->phdr.p_align;
+ }
+ }
+ }
+
+ CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
+ if (ms->shdr.sh_offset >= offset) {
+ if (ms->shdr.sh_addralign > align) {
+ align = ms->shdr.sh_addralign;
+ }
+ }
+ }
+
+ size = ROUNDUP(size, align);
+
+ /* Shift stuff */
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ if (mp->phdr.p_type != PT_LOAD) {
+ continue;
+ }
+
+ if (mp->phdr.p_offset >= offset) {
+ mp->phdr.p_offset += size;
+
+ elfu_mPhdrUpdateChildOffsets(mp);
+ }
+ }
+
+ CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
+ if (ms->shdr.sh_offset >= offset) {
+ ms->shdr.sh_offset += size;
+ }
+ }
+
+ if (me->ehdr.e_phoff >= offset) {
+ me->ehdr.e_phoff += size;
+ }
+
+ if (me->ehdr.e_shoff >= offset) {
+ me->ehdr.e_shoff += size;
+ }
+
+ return size;
+}
+
+
+
+
+
+/* Finds a suitable PHDR to insert a hole into and expands it
+ * if necessary.
+ * Returns memory address the hole will be mapped to, or 0 if
+ * the operation failed. */
+GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
+ GElf_Word align, int w, int x,
+ ElfuPhdr **injPhdr)
+{
+ ElfuPhdr *first = NULL;
+ ElfuPhdr *last = NULL;
+ ElfuPhdr *mp;
+
+ assert(!(w && x));
+
+ /* Treat read-only data as executable.
+ * That's what the GNU toolchain does on x86. */
+ if (!w && !x) {
+ x = 1;
+ }
+
+ /* Find first and last LOAD PHDRs.
+ * Don't compare p_memsz - segments don't overlap in memory. */
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ if (mp->phdr.p_type != PT_LOAD) {
+ continue;
+ }
+ if (!first || mp->phdr.p_vaddr < first->phdr.p_vaddr) {
+ first = mp;
+ }
+ if (!last || mp->phdr.p_vaddr > last->phdr.p_vaddr) {
+ last = mp;
+ }
+ }
+
+ if ((w && (last->phdr.p_flags & PF_W))
+ || (x && (last->phdr.p_flags & PF_X))) {
+ /* Need to append. */
+ GElf_Off injOffset = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz);
+ GElf_Word injSpace = 0;
+ GElf_Word nobitsize = last->phdr.p_memsz - last->phdr.p_filesz;
+
+ /* Expand NOBITS if any */
+ if (nobitsize > 0) {
+ GElf_Off endOff = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz);
+ GElf_Off endAddr = OFFS_END(last->phdr.p_vaddr, last->phdr.p_filesz);
+ ElfuScn *ms;
+
+ ELFU_INFO("Expanding NOBITS at address 0x%x...\n", (unsigned)endAddr);
+
+ CIRCLEQ_FOREACH(ms, &last->childScnList, elemChildScn) {
+ if (ms->shdr.sh_offset == endOff) {
+ assert(ms->shdr.sh_type == SHT_NOBITS);
+ assert(ms->shdr.sh_size == nobitsize);
+ ms->data.d_buf = malloc(ms->shdr.sh_size);
+ memset(ms->data.d_buf, '\0', ms->shdr.sh_size);
+ if (!ms->data.d_buf) {
+ ELFU_WARN("mExpandNobits: Could not allocate %u bytes for NOBITS expansion. Data may be inconsistent.\n",
+ (unsigned)ms->shdr.sh_size);
+ assert(0);
+ goto ERROR;
+ }
+
+ ms->data.d_align = 1;
+ ms->data.d_off = 0;
+ ms->data.d_type = ELF_T_BYTE;
+ ms->data.d_size = ms->shdr.sh_size;
+ ms->data.d_version = elf_version(EV_NONE);
+
+ ms->shdr.sh_type = SHT_PROGBITS;
+ ms->shdr.sh_addr = endAddr;
+ }
+ }
+
+ injSpace += shiftStuffAtAfterOffset(me, endOff, nobitsize);
+ injSpace -= nobitsize;
+ injOffset += nobitsize;
+ last->phdr.p_filesz += nobitsize;
+ assert(last->phdr.p_filesz == last->phdr.p_memsz);
+ }
+
+ /* Calculate how much space we need, taking alignment into account */
+ size += ROUNDUP(injOffset, align) - injOffset;
+
+ /* If there is not enough space left, create even more. */
+ if (injSpace < size) {
+ injSpace += shiftStuffAtAfterOffset(me, injOffset, size - injSpace);
+ }
+ assert(injSpace >= size);
+
+ /* Remap ourselves */
+ last->phdr.p_filesz += size;
+ last->phdr.p_memsz += size;
+
+ injOffset = ROUNDUP(injOffset, align);
+
+ if (injPhdr) {
+ *injPhdr = last;
+ }
+ return last->phdr.p_vaddr + (injOffset - last->phdr.p_offset);
+ } else if ((w && (first->phdr.p_flags & PF_W))
+ || (x && (first->phdr.p_flags & PF_X))) {
+ /* Need to prepend or split up the PHDR. */
+ GElf_Off injOffset = OFFS_END(first->phdr.p_offset, first->phdr.p_filesz);
+ ElfuScn *ms;
+
+ /* Round up size to take PHDR alignment into account.
+ * We assume that this is a multiple of the alignment asked for. */
+ assert(first->phdr.p_align >= align);
+ size = ROUNDUP(size, first->phdr.p_align);
+
+ /* Find first section. We assume there is at least one. */
+ assert(!CIRCLEQ_EMPTY(&first->childScnList));
+ injOffset = CIRCLEQ_FIRST(&first->childScnList)->shdr.sh_offset;
+
+ /* Move our sections */
+ CIRCLEQ_FOREACH(ms, &first->childScnList, elemChildScn) {
+ if (ms->shdr.sh_offset >= injOffset) {
+ ms->shdr.sh_offset += size;
+ }
+ }
+
+ /* Move our PHDRs */
+ CIRCLEQ_FOREACH(mp, &first->childPhdrList, elemChildPhdr) {
+ if (mp->phdr.p_offset >= injOffset) {
+ mp->phdr.p_offset += size;
+ } else {
+ mp->phdr.p_vaddr -= size;
+ mp->phdr.p_paddr -= size;
+ }
+ }
+
+ /* Move other PHDRs and sections */
+ assert(size <= shiftStuffAtAfterOffset(me, injOffset, size));
+
+ /* Remap ourselves */
+ first->phdr.p_vaddr -= size;
+ first->phdr.p_paddr -= size;
+ first->phdr.p_filesz += size;
+ first->phdr.p_memsz += size;
+
+ injOffset = ROUNDUP(injOffset, align);
+
+ if (injPhdr) {
+ *injPhdr = first;
+ }
+ return first->phdr.p_vaddr + (injOffset - first->phdr.p_offset);
+ }
+
+ ERROR:
+ if (injPhdr) {
+ *injPhdr = NULL;
+ }
+ return 0;
+}
+
+
+
+
+static int cmpPhdrOffs(const void *mp1, const void *mp2)
+{
+ ElfuPhdr *p1;
+ ElfuPhdr *p2;
+
+ assert(mp1);
+ assert(mp2);
+
+ p1 = *(ElfuPhdr**)mp1;
+ p2 = *(ElfuPhdr**)mp2;
+
+ assert(p1);
+ assert(p2);
+
+
+ if (p1->phdr.p_offset < p2->phdr.p_offset) {
+ return -1;
+ } else if (p1->phdr.p_offset == p2->phdr.p_offset) {
+ return 0;
+ } else /* if (p1->phdr.p_offset > p2->phdr.p_offset) */ {
+ return 1;
+ }
+}
+
+int elfu_mLayoutAuto(ElfuElf *me)
+{
+ ElfuPhdr *mp;
+ ElfuScn *ms;
+ ElfuPhdr **phdrArr;
+ GElf_Off lastend = 0;
+ size_t i, j;
+
+ assert(me);
+
+ phdrArr = malloc(elfu_mPhdrCount(me) * sizeof(*phdrArr));
+ if (!phdrArr) {
+ ELFU_WARN("elfu_mLayoutAuto: malloc failed for phdrArr.\n");
+ return 1;
+ }
+
+ i = 0;
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ if (mp->phdr.p_type != PT_LOAD) {
+ continue;
+ }
+
+ phdrArr[i] = mp;
+ i++;
+ }
+
+ /* Assume we have at least one LOAD PHDR,
+ * and that it ends after EHDR and PHDRs */
+ assert(i > 1);
+
+ /* Sort array by file offset */
+ qsort(phdrArr, i, sizeof(*phdrArr), cmpPhdrOffs);
+
+ lastend = OFFS_END(phdrArr[0]->phdr.p_offset, phdrArr[0]->phdr.p_filesz);
+
+ /* Wiggle offsets of 2nd, 3rd etc so take minimum space */
+ for (j = 1; j < i; j++) {
+ GElf_Off subalign = phdrArr[j]->phdr.p_offset % phdrArr[j]->phdr.p_align;
+
+ if ((lastend % phdrArr[j]->phdr.p_align) <= subalign) {
+ lastend += subalign - (lastend % phdrArr[j]->phdr.p_align);
+ } else {
+ lastend += phdrArr[j]->phdr.p_align - ((lastend % phdrArr[j]->phdr.p_align) - subalign);
+ }
+
+ phdrArr[j]->phdr.p_offset = lastend;
+
+ elfu_mPhdrUpdateChildOffsets(phdrArr[j]);
+
+ lastend = OFFS_END(phdrArr[j]->phdr.p_offset, phdrArr[j]->phdr.p_filesz);
+ }
+
+ free(phdrArr);
+
+
+ /* Place orphaned sections afterwards, maintaining alignment */
+ CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
+ lastend = ROUNDUP(lastend, ms->shdr.sh_addralign);
+
+ ms->shdr.sh_offset = lastend;
+
+ lastend = OFFS_END(ms->shdr.sh_offset, SCNFILESIZE(&ms->shdr));
+ }
+
+
+ /* Move SHDRs to end */
+ lastend = ROUNDUP(lastend, 8);
+ me->ehdr.e_shoff = lastend;
+
+
+ return 0;
+}
--- /dev/null
+#include <assert.h>
+#include <libelfu/libelfu.h>
+
+
+size_t elfu_mPhdrCount(ElfuElf *me)
+{
+ ElfuPhdr *mp;
+ size_t i = 0;
+
+ assert(me);
+
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ i++;
+ }
+
+ return i;
+}
+
+
+
+void elfu_mPhdrUpdateChildOffsets(ElfuPhdr *mp)
+{
+ ElfuScn *ms;
+ ElfuPhdr *mpc;
+
+ assert(mp);
+ assert(mp->phdr.p_type == PT_LOAD);
+
+ CIRCLEQ_FOREACH(mpc, &mp->childPhdrList, elemChildPhdr) {
+ mpc->phdr.p_offset = mp->phdr.p_offset + (mpc->phdr.p_vaddr - mp->phdr.p_vaddr);
+ }
+
+ CIRCLEQ_FOREACH(ms, &mp->childScnList, elemChildScn) {
+ ms->shdr.sh_offset = mp->phdr.p_offset + (ms->shdr.sh_addr - mp->phdr.p_vaddr);
+ }
+}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <libelfu/libelfu.h>
+
+
+static int appendData(ElfuScn *ms, void *buf, size_t len)
+{
+ char *newbuf;
+
+ assert(ms);
+ assert(ms->shdr.sh_type != SHT_NOBITS);
+ assert(ms->data.d_buf);
+
+ newbuf = realloc(ms->data.d_buf, ms->shdr.sh_size + len);
+ if (!newbuf) {
+ ELFU_WARN("appendData: malloc() failed for newbuf.\n");
+ return 1;
+ }
+
+ ms->data.d_buf = newbuf;
+ memcpy(newbuf + ms->shdr.sh_size, buf, len);
+ ms->shdr.sh_size += len;
+ ms->data.d_size += len;
+ assert(ms->shdr.sh_size == ms->data.d_size);
+
+ return 0;
+}
+
+
+static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *oldscn)
+{
+ ElfuScn *newscn = NULL;
+ GElf_Addr injAddr;
+ GElf_Off injOffset;
+ ElfuPhdr *injPhdr;
+
+ if (oldscn->shdr.sh_flags & SHF_ALLOC) {
+ newscn = elfu_mCloneScn(oldscn);
+ if (!newscn) {
+ return NULL;
+ }
+
+ if (newscn->shdr.sh_type == SHT_NOBITS) {
+ /* Expand this to SHT_PROGBITS, then insert as such. */
+
+ assert(!newscn->data.d_buf);
+
+ newscn->data.d_buf = malloc(newscn->shdr.sh_size);
+ if (!newscn->data.d_buf) {
+ goto ERROR;
+ }
+ newscn->data.d_size = newscn->shdr.sh_size;
+ newscn->shdr.sh_type = SHT_PROGBITS;
+ }
+
+ injAddr = elfu_mLayoutGetSpaceInPhdr(me,
+ newscn->shdr.sh_size,
+ newscn->shdr.sh_addralign,
+ newscn->shdr.sh_flags & SHF_WRITE,
+ newscn->shdr.sh_flags & SHF_EXECINSTR,
+ &injPhdr);
+
+ if (!injPhdr) {
+ ELFU_WARN("insertSection: Could not find a place to insert section.\n");
+ goto ERROR;
+ }
+
+ ELFU_INFO("Inserting %s at address 0x%x...\n",
+ elfu_mScnName(mrel, oldscn),
+ (unsigned)injAddr);
+
+ injOffset = injAddr - injPhdr->phdr.p_vaddr + injPhdr->phdr.p_offset;
+
+ newscn->shdr.sh_addr = injAddr;
+ newscn->shdr.sh_offset = injOffset;
+
+ if (CIRCLEQ_EMPTY(&injPhdr->childScnList)
+ || CIRCLEQ_LAST(&injPhdr->childScnList)->shdr.sh_offset < injOffset) {
+ CIRCLEQ_INSERT_TAIL(&injPhdr->childScnList, newscn, elemChildScn);
+ } else {
+ ElfuScn *ms;
+ CIRCLEQ_FOREACH(ms, &injPhdr->childScnList, elemChildScn) {
+ if (injOffset < ms->shdr.sh_offset) {
+ CIRCLEQ_INSERT_BEFORE(&injPhdr->childScnList, ms, newscn, elemChildScn);
+ break;
+ }
+ }
+ }
+
+
+ /* Inject name */
+ if (me->shstrtab) {
+ char *newname;
+ size_t newnamelen;
+
+ newnamelen = strlen("reladd") + 1;
+ if (elfu_mScnName(mrel, oldscn)) {
+ newnamelen += strlen(elfu_mScnName(mrel, oldscn));
+ }
+
+ newname = malloc(newnamelen);
+ strcpy(newname, "reladd");
+ strcat(newname, elfu_mScnName(mrel, oldscn));
+
+ if (!newname) {
+ ELFU_WARN("insertSection: malloc() failed for newname. Leaving section name empty.\n");
+ newscn->shdr.sh_name = 0;
+ } else {
+ size_t offset = me->shstrtab->shdr.sh_size;
+
+ if (!appendData(me->shstrtab, newname, newnamelen)) {
+ newscn->shdr.sh_name = offset;
+ }
+
+ free(newname);
+ }
+ }
+
+ return newscn;
+ } else {
+ ELFU_WARN("insertSection: Skipping non-memory section %s (type %d flags %u).\n",
+ elfu_mScnName(mrel, oldscn),
+ oldscn->shdr.sh_type,
+ (unsigned)oldscn->shdr.sh_flags);
+ goto ERROR;
+ }
+
+ ERROR:
+ if (newscn) {
+ // TODO: Destroy newscn
+ }
+ return NULL;
+}
+
+
+static void* subScnAdd1(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2)
+{
+ ElfuScn *newscn;
+ ElfuElf *me = (ElfuElf*)aux1;
+ (void)aux2;
+
+
+ switch(ms->shdr.sh_type) {
+ case SHT_PROGBITS: /* 1 */
+ case SHT_NOBITS: /* 8 */
+ /* Ignore empty sections */
+ if (ms->shdr.sh_size == 0) {
+ break;
+ }
+
+ /* Find a place where it belongs and shove it in. */
+ newscn = insertSection(me, mrel, ms);
+ if (!newscn) {
+ ELFU_WARN("mReladd: Could not insert section %s (type %d), skipping.\n",
+ elfu_mScnName(mrel, ms),
+ ms->shdr.sh_type);
+ }
+ break;
+ }
+
+ return NULL;
+}
+
+
+static void* subScnAdd2(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2)
+{
+ ElfuElf *me = (ElfuElf*)aux1;
+ (void)aux2;
+
+ switch(ms->shdr.sh_type) {
+ case SHT_NULL: /* 0 */
+ case SHT_PROGBITS: /* 1 */
+ case SHT_STRTAB: /* 3 */
+ case SHT_NOBITS: /* 8 */
+ break;
+
+
+ case SHT_REL: /* 9 */
+ /* Relocate. */
+ elfu_mRelocate(me, elfu_mScnByOldscn(me, ms->infoptr), ms);
+ break;
+
+ case SHT_RELA: /* 4 */
+ // TODO: Needs a parser
+ //elfu_mRelocate(elfu_mScnByOldscn(me, ms->infoptr), ms);
+
+ case SHT_SYMTAB: /* 2 */
+ /* Merge with the existing table. Take care of string tables also. */
+
+ /* The next section types either do not occur in .o files, or are
+ * not strictly necessary to process here. */
+ case SHT_NOTE: /* 7 */
+ case SHT_HASH: /* 5 */
+ case SHT_DYNAMIC: /* 6 */
+ case SHT_SHLIB: /* 10 */
+ case SHT_DYNSYM: /* 11 */
+ case SHT_INIT_ARRAY: /* 14 */
+ case SHT_FINI_ARRAY: /* 15 */
+ case SHT_PREINIT_ARRAY: /* 16 */
+ case SHT_GROUP: /* 17 */
+ case SHT_SYMTAB_SHNDX: /* 18 */
+ case SHT_NUM: /* 19 */
+ default:
+ ELFU_WARN("mReladd: Skipping section %s (type %d).\n",
+ elfu_mScnName(mrel, ms),
+ ms->shdr.sh_type);
+ }
+
+ return NULL;
+}
+
+
+
+static void insertSymClone(ElfuElf *me, const ElfuScn *oldmsst, const ElfuSym *oldsym)
+{
+ GElf_Xword newsize;
+ char *newbuf;
+ ElfuScn *newscn = NULL;
+ ElfuSym *newsym;
+ char *oldsymname;
+
+ assert(me);
+ assert(oldmsst);
+ assert(oldsym);
+
+ /* If the old symbol pointed to a section, find its clone in the target */
+ if (oldsym->scnptr) {
+ newscn = elfu_mScnByOldscn(me, oldsym->scnptr);
+
+ /* If we didn't copy the section referenced, we won't
+ * copy this symbol either */
+ if (!newscn) {
+ return;
+ }
+ }
+
+ // TODO: Allocate symtab if none present
+ assert(me->symtab);
+
+ /* Allocate memory for the cloned symbol */
+ newsym = malloc(sizeof(*newsym));
+ if (!newsym) {
+ ELFU_WARN("insertSymClone: malloc() failed for newsym.\n");
+ goto ERROR;
+ }
+
+ oldsymname = ELFU_SYMSTR(oldmsst, oldsym->name);
+
+ /* Expand .strtab, append symbol name, link newsym to it */
+ newsize = me->symtab->linkptr->shdr.sh_size + strlen(oldsymname) + 1;
+ newbuf = realloc(me->symtab->linkptr->data.d_buf, newsize);
+ if (!newbuf) {
+ ELFU_WARN("insertSymClone: realloc() failed for strtab.\n");
+ goto ERROR;
+ }
+
+ me->symtab->linkptr->data.d_buf = newbuf;
+
+ newsym->name = me->symtab->linkptr->shdr.sh_size;
+
+ strcpy(newbuf + newsym->name, oldsymname);
+
+ me->symtab->linkptr->data.d_size = newsize;
+ me->symtab->linkptr->shdr.sh_size = newsize;
+
+
+ /* Copy all other fields */
+ newsym->scnptr = newscn;
+ newsym->shndx = oldsym->shndx; /* If scnptr == NULL, this becomes relevant */
+ newsym->bind = oldsym->bind;
+ newsym->other = oldsym->other;
+ newsym->size = oldsym->size;
+ newsym->type = oldsym->type;
+ newsym->value = oldsym->value;
+
+ /* In executables, symbol addresses need to be in memory */
+ if (newscn) {
+ newsym->value += newscn->shdr.sh_addr;
+ }
+
+ /* Insert symbol */
+ CIRCLEQ_INSERT_TAIL(&me->symtab->symtab.syms, newsym, elem);
+
+ return;
+
+ ERROR:
+ if (newsym) {
+ free(newsym);
+ }
+}
+
+static void mergeSymtab(ElfuElf *me, const ElfuElf *mrel)
+{
+ ElfuSym *sym;
+
+ assert(me);
+ assert(mrel);
+
+ CIRCLEQ_FOREACH(sym, &mrel->symtab->symtab.syms, elem) {
+ insertSymClone(me, mrel->symtab, sym);
+ }
+}
+
+
+
+void elfu_mReladd(ElfuElf *me, const ElfuElf *mrel)
+{
+ assert(me);
+ assert(mrel);
+
+ /* For each section in object file, guess how to insert it */
+ elfu_mScnForall((ElfuElf*)mrel, subScnAdd1, me, NULL);
+
+ mergeSymtab(me, mrel);
+
+ /* Do relocations and other stuff */
+ elfu_mScnForall((ElfuElf*)mrel, subScnAdd2, me, NULL);
+
+ /* Re-layout to accommodate new contents */
+ elfu_mLayoutAuto(me);
+}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libelfu/libelfu.h>
+
+
+/* Apply relocation information from section *msrt to data in
+ * section *mstarget (which is stored in *metarget). */
+void elfu_mRelocate(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt)
+{
+ ElfuRel *rel;
+
+ assert(mstarget);
+ assert(msrt);
+
+ ELFU_DEBUG("Relocating in section of type %u size %x\n",
+ mstarget->shdr.sh_type,
+ (unsigned)mstarget->shdr.sh_size);
+
+ CIRCLEQ_FOREACH(rel, &msrt->reltab.rels, elem) {
+ Elf32_Word *dest32 = (Elf32_Word*)(((char*)(mstarget->data.d_buf)) + rel->offset);
+ Elf64_Word *dest64 = (Elf64_Word*)(((char*)(mstarget->data.d_buf)) + rel->offset);
+
+
+ if (metarget->elfclass == ELFCLASS32) {
+ Elf32_Word a32 = rel->addendUsed ? rel->addend : *dest32;
+ Elf32_Addr p32 = mstarget->shdr.sh_addr + rel->offset;
+ Elf32_Addr s32 = elfu_mSymtabLookupVal(metarget, msrt->linkptr, rel->sym);
+ switch(rel->type) {
+ case R_386_NONE:
+ ELFU_DEBUG("Skipping relocation: R_386_NONE\n");
+ break;
+ case R_386_32:
+ *dest32 = s32 + a32;
+ break;
+ case R_386_PC32:
+ *dest32 = s32 + a32 - p32;
+ break;
+
+ default:
+ ELFU_DEBUG("Skipping relocation: Unknown type %d\n", rel->type);
+ }
+ } else if (metarget->elfclass == ELFCLASS64) {
+ Elf64_Word a64 = rel->addend;
+ Elf64_Addr p64 = mstarget->shdr.sh_addr + rel->offset;
+ Elf64_Addr s64 = elfu_mSymtabLookupVal(metarget, msrt->linkptr, rel->sym);
+
+ /* x86-64 only uses RELA with explicit addend. */
+ assert(rel->addendUsed);
+
+ switch(rel->type) {
+ case R_X86_64_NONE:
+ ELFU_DEBUG("Skipping relocation: R_386_NONE\n");
+ break;
+ case R_X86_64_64:
+ *dest64 = s64 + a64;
+ break;
+ case R_X86_64_PC32:
+ *dest32 = s64 + a64 - p64;
+ break;
+ case R_X86_64_32:
+ *dest32 = s64 + a64;
+ break;
+
+ default:
+ ELFU_DEBUG("Skipping relocation: Unknown type %d", rel->type);
+ }
+ }
+ }
+}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <libelfu/libelfu.h>
+
+
+/* Meta-functions */
+
+void* elfu_mScnForall(ElfuElf *me, SectionHandlerFunc f, void *aux1, void *aux2)
+{
+ ElfuPhdr *mp;
+ ElfuScn *ms;
+
+ // TODO: Sort PHDRs by offset before interating
+
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ if (mp->phdr.p_type != PT_LOAD) {
+ continue;
+ }
+
+ CIRCLEQ_FOREACH(ms, &mp->childScnList, elemChildScn) {
+ void *rv = f(me, ms, aux1, aux2);
+
+ if (rv) {
+ return rv;
+ }
+ }
+ }
+
+ CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
+ void *rv = f(me, ms, aux1, aux2);
+
+ if (rv) {
+ return rv;
+ }
+ }
+
+ return NULL;
+}
+
+
+
+
+/* Counting */
+
+static void* subCounter(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
+{
+ size_t *i = (size_t*)aux1;
+ ElfuScn *otherScn = (ElfuScn*)aux2;
+
+ if (ms == otherScn) {
+ return ms;
+ }
+
+ *i += 1;
+
+ /* Continue */
+ return NULL;
+}
+
+
+size_t elfu_mScnCount(ElfuElf *me)
+{
+ /* NULL section *is not* counted */
+ size_t i = 0;
+
+ assert(me);
+
+ elfu_mScnForall(me, subCounter, &i, NULL);
+
+ return i;
+}
+
+
+/* Returns the index a section would have in the flattened ELF */
+size_t elfu_mScnIndex(ElfuElf *me, ElfuScn *ms)
+{
+ /* NULL section *is* counted */
+ size_t i = 1;
+
+ assert(me);
+ assert(ms);
+
+ elfu_mScnForall(me, subCounter, &i, ms);
+
+ /* If this assertion is broken then ms is not a section in me. */
+ assert(i <= elfu_mScnCount(me));
+ return i;
+}
+
+
+static void* subOldscn(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
+{
+ ElfuScn *otherScn = (ElfuScn*)aux1;
+ (void)aux2;
+
+ if (ms->oldptr == otherScn) {
+ return ms;
+ }
+
+ /* Continue */
+ return NULL;
+}
+
+/* Returns the section with oldscn == oldscn */
+ElfuScn* elfu_mScnByOldscn(ElfuElf *me, ElfuScn *oldscn)
+{
+ assert(me);
+ assert(oldscn);
+
+ return elfu_mScnForall(me, subOldscn, oldscn, NULL);
+}
+
+
+
+
+/* Convenience */
+
+char* elfu_mScnName(ElfuElf *me, ElfuScn *ms)
+{
+ assert(me);
+ assert(ms);
+
+ if (!me->shstrtab) {
+ return NULL;
+ }
+
+ if (!me->shstrtab->data.d_buf) {
+ return NULL;
+ }
+
+ return &((char*)me->shstrtab->data.d_buf)[ms->shdr.sh_name];
+}
+
+
+static void* subScnsToArray(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
+{
+ ElfuScn **arr = (ElfuScn**)aux1;
+ size_t *i = (size_t*)aux2;
+
+ arr[(*i)] = ms;
+ *i += 1;
+
+ /* Continue */
+ return NULL;
+}
+
+static int cmpScnOffs(const void *ms1, const void *ms2)
+{
+ ElfuScn *s1;
+ ElfuScn *s2;
+
+ assert(ms1);
+ assert(ms2);
+
+ s1 = *(ElfuScn**)ms1;
+ s2 = *(ElfuScn**)ms2;
+
+ assert(s1);
+ assert(s2);
+
+
+ if (s1->shdr.sh_offset < s2->shdr.sh_offset) {
+ return -1;
+ } else if (s1->shdr.sh_offset == s2->shdr.sh_offset) {
+ return 0;
+ } else /* if (s1->shdr.sh_offset > s2->shdr.sh_offset) */ {
+ return 1;
+ }
+}
+
+ElfuScn** elfu_mScnSortedByOffset(ElfuElf *me, size_t *count)
+{
+ size_t numSecs;
+ ElfuScn **sortedSecs;
+ size_t i;
+
+ assert(me);
+
+ /* Sort sections by offset in file */
+ numSecs = elfu_mScnCount(me);
+ sortedSecs = malloc(numSecs * sizeof(*sortedSecs));
+ if (!sortedSecs) {
+ ELFU_WARN("elfu_mScnSortedByOffset: Failed to allocate memory.\n");
+ return NULL;
+ }
+
+ i = 0;
+ elfu_mScnForall(me, subScnsToArray, sortedSecs, &i);
+ assert(i == numSecs);
+
+ qsort(sortedSecs, numSecs, sizeof(*sortedSecs), cmpScnOffs);
+
+ *count = numSecs;
+
+ return sortedSecs;
+}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libelfu/libelfu.h>
+
+
+static void* subFindByName(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
+{
+ char *name = (char*)aux1;
+ (void)aux2;
+
+ if (elfu_mScnName(me, ms)) {
+ if (!strcmp(elfu_mScnName(me, ms), name)) {
+ return ms;
+ }
+ }
+
+ /* Continue */
+ return NULL;
+}
+
+/* Hazard a guess where a function may be found in the PLT */
+static GElf_Word pltLookupVal(ElfuElf *me, char *name)
+{
+ ElfuScn *relplt;
+ ElfuScn *plt;
+ ElfuRel *rel;
+ GElf_Word j;
+
+ relplt = elfu_mScnForall(me, subFindByName, ".rel.plt", NULL);
+ if (!relplt) {
+ ELFU_WARN("dynsymLookupVal: Could not find .rel.plt section in destination ELF.\n");
+ return 0;
+ }
+
+ plt = elfu_mScnForall(me, subFindByName, ".plt", NULL);
+ if (!plt) {
+ ELFU_WARN("dynsymLookupVal: Could not find .plt section in destination ELF.\n");
+ return 0;
+ }
+
+
+ /* Look up name. If the j-th entry in .rel.plt has the name we are
+ * looking for, we assume that the (j+1)-th entry in .plt is machine
+ * code to jump to the function.
+ * Your mileage may vary, but it works on my GNU binaries. */
+ assert(relplt->linkptr);
+ j = 0;
+ CIRCLEQ_FOREACH(rel, &relplt->reltab.rels, elem) {
+ GElf_Word i;
+ ElfuSym *sym;
+ char *symname;
+
+ j++;
+
+ /* We only consider runtime relocations for functions.
+ * Technically, these relocations write the functions' addresses
+ * to the GOT, not the PLT, after the dynamic linker has found
+ * them. */
+ if ((me->elfclass == ELFCLASS32 && rel->type != R_386_JMP_SLOT)
+ || (me->elfclass == ELFCLASS64 && rel->type != R_X86_64_JUMP_SLOT)) {
+ continue;
+ }
+
+ /* Get the (rel->sym)-th symbol from the symbol table that
+ * .rel.plt points to. */
+ sym = CIRCLEQ_FIRST(&relplt->linkptr->symtab.syms);
+ for (i = 1; i < rel->sym; i++) {
+ sym = CIRCLEQ_NEXT(sym, elem);
+ }
+
+ symname = ELFU_SYMSTR(relplt->linkptr, sym->name);
+ if (!strcmp(symname, name)) {
+ /* If this is the symbol we are looking for, then in an x86 binary
+ * the jump to the dynamic symbol is probably at offset (j * 16)
+ * from the start of the PLT, where j is the PLT entry and 16 is
+ * the number of bytes the machine code in a PLT entry take. */
+ GElf_Addr addr = plt->shdr.sh_addr + (16 * j);
+ ELFU_DEBUG("dynsymLookupVal: Guessing symbol '%s' is in destination memory at %x (PLT entry #%u).\n", name, (unsigned)addr, j);
+ return addr;
+ }
+ }
+
+ ELFU_WARN("dynsymLookupVal: Could not find symbol '%s' in destination ELF.\n", name);
+
+ return 0;
+}
+
+
+
+/* Look up a value in the symbol table section *msst which is in *me.
+ * If it is not found there, see if we can find it in *me's PLT. */
+GElf_Word elfu_mSymtabLookupVal(ElfuElf *me, ElfuScn *msst, GElf_Word entry)
+{
+ GElf_Word i;
+ ElfuSym *sym;
+ char *symname;
+
+ assert(me);
+ assert(msst);
+ assert(entry > 0);
+ assert(!CIRCLEQ_EMPTY(&msst->symtab.syms));
+
+ sym = CIRCLEQ_FIRST(&msst->symtab.syms);
+ for (i = 1; i < entry; i++) {
+ sym = CIRCLEQ_NEXT(sym, elem);
+ }
+ symname = ELFU_SYMSTR(msst, sym->name);
+
+ switch (sym->type) {
+ case STT_NOTYPE:
+ case STT_OBJECT:
+ case STT_FUNC:
+ if (sym->scnptr) {
+ ElfuScn *newscn = elfu_mScnByOldscn(me, sym->scnptr);
+ assert(newscn);
+ return newscn->shdr.sh_addr + sym->value;
+ } else if (sym->shndx == SHN_UNDEF) {
+ /* Look the symbol up in .rel.plt. If it cannot be found there then
+ * .rel.dyn may need to be expanded with a COPY relocation so the
+ * dynamic linker fixes up the (TODO). */
+ return pltLookupVal(me, symname);
+ } else if (sym->shndx == SHN_ABS) {
+ return sym->value;
+ } else {
+ ELFU_WARN("symtabLookupVal: Symbol binding COMMON is not supported, using 0.\n");
+ return 0;
+ }
+ break;
+ case STT_SECTION:
+ assert(sym->scnptr);
+ assert(elfu_mScnByOldscn(me, sym->scnptr));
+ return elfu_mScnByOldscn(me, sym->scnptr)->shdr.sh_addr;
+ case STT_FILE:
+ ELFU_WARN("symtabLookupVal: Symbol type FILE is not supported, using 0.\n");
+ return 0;
+ default:
+ ELFU_WARN("symtabLookupVal: Unknown symbol type %d for %s.\n", sym->type, symname);
+ return 0;
+ }
+}
+
+
+/* Look up a value in the symbol table section *msst which is in *me. */
+GElf_Word elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name)
+{
+ ElfuSym *sym;
+
+ assert(me);
+ assert(msst);
+ assert(name);
+ assert(strlen(name) > 0);
+ assert(!CIRCLEQ_EMPTY(&msst->symtab.syms));
+
+ CIRCLEQ_FOREACH(sym, &msst->symtab.syms, elem) {
+ char *symname = ELFU_SYMSTR(msst, sym->name);
+
+ if (!strcmp(symname, name)) {
+ goto SYMBOL_FOUND;
+ }
+ }
+ return 0;
+
+
+ SYMBOL_FOUND:
+
+ switch (sym->type) {
+ case STT_NOTYPE:
+ case STT_OBJECT:
+ case STT_FUNC:
+ if (sym->scnptr) {
+ GElf_Addr a = sym->value;
+ a += me->ehdr.e_type == ET_REL ? sym->scnptr->shdr.sh_addr : 0;
+ return a;
+ } else if (sym->shndx == SHN_UNDEF) {
+ return 0;
+ } else if (sym->shndx == SHN_ABS) {
+ return sym->value;
+ } else {
+ ELFU_WARN("elfu_mSymtabLookupAddrByName: Symbol binding COMMON is not supported, using 0.\n");
+ return 0;
+ }
+ break;
+ default:
+ return 0;
+ }
+}
+
+
+
+/* Convert symtab from memory model to elfclass specific format */
+void elfu_mSymtabFlatten(ElfuElf *me)
+{
+ ElfuSym *sym;
+ size_t numsyms = 0;
+
+ elfu_mLayoutAuto(me);
+
+ /* Update section indexes and count symbols */
+ CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) {
+ if (sym->scnptr) {
+ sym->shndx = elfu_mScnIndex(me, sym->scnptr);
+ }
+
+ numsyms++;
+ }
+
+ /* Copy symbols to elfclass-specific format */
+ if (me->elfclass == ELFCLASS32) {
+ size_t newsize = (numsyms + 1) * sizeof(Elf32_Sym);
+ size_t i;
+
+ if (me->symtab->data.d_buf) {
+ free(me->symtab->data.d_buf);
+ }
+ me->symtab->data.d_buf = malloc(newsize);
+ assert(me->symtab->data.d_buf);
+
+ me->symtab->data.d_size = newsize;
+ me->symtab->shdr.sh_size = newsize;
+ memset(me->symtab->data.d_buf, 0, newsize);
+
+ i = 1;
+ CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) {
+ Elf32_Sym *es = ((Elf32_Sym*)me->symtab->data.d_buf) + i;
+
+ es->st_name = sym->name;
+ es->st_value = sym->value;
+ es->st_size = sym->size;
+ es->st_info = ELF32_ST_INFO(sym->bind, sym->type);
+ es->st_other = sym->other;
+ es->st_shndx = sym->shndx;
+
+ i++;
+ }
+ } else if (me->elfclass == ELFCLASS64) {
+ size_t newsize = (numsyms + 1) * sizeof(Elf64_Sym);
+ size_t i;
+
+ if (me->symtab->data.d_buf) {
+ free(me->symtab->data.d_buf);
+ }
+ me->symtab->data.d_buf = malloc(newsize);
+ assert(me->symtab->data.d_buf);
+
+ me->symtab->data.d_size = newsize;
+ me->symtab->shdr.sh_size = newsize;
+ memset(me->symtab->data.d_buf, 0, newsize);
+
+ i = 1;
+ CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) {
+ Elf64_Sym *es = ((Elf64_Sym*)me->symtab->data.d_buf) + i;
+
+ es->st_name = sym->name;
+ es->st_value = sym->value;
+ es->st_size = sym->size;
+ es->st_info = ELF64_ST_INFO(sym->bind, sym->type);
+ es->st_other = sym->other;
+ es->st_shndx = sym->shndx;
+
+ i++;
+ }
+ } else {
+ /* Unknown elfclass */
+ assert(0);
+ }
+
+ elfu_mLayoutAuto(me);
+}
--- /dev/null
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libelfu/libelfu.h>
+
+
+static void modelToPhdrs(ElfuElf *me, Elf *e)
+{
+ ElfuPhdr *mp;
+ size_t i;
+
+ /* Count PHDRs */
+ i = 0;
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ i++;
+ }
+
+ if (!gelf_newphdr(e, i)) {
+ ELFU_WARNELF("gelf_newphdr");
+ }
+
+ /* Copy PHDRs */
+ i = 0;
+ CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
+ if (!gelf_update_phdr (e, i, &mp->phdr)) {
+ ELFU_WARNELF("gelf_update_phdr");
+ }
+
+ i++;
+ }
+}
+
+
+
+static void* modelToSection(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
+{
+ Elf_Scn *scnOut;
+ Elf *e = (Elf*)aux1;
+ (void) me;
+ (void) aux2;
+
+ scnOut = elf_newscn(e);
+ if (!scnOut) {
+ ELFU_WARNELF("elf_newscn");
+ return (void*)-1;
+ }
+
+
+ /* SHDR */
+ if (ms->linkptr) {
+ ms->shdr.sh_link = elfu_mScnIndex(me, ms->linkptr);
+ }
+ if (ms->infoptr) {
+ ms->shdr.sh_info = elfu_mScnIndex(me, ms->infoptr);
+ }
+ if (!gelf_update_shdr(scnOut, &ms->shdr)) {
+ ELFU_WARNELF("gelf_update_shdr");
+ }
+
+
+ /* Data */
+ if (ms->data.d_buf) {
+ Elf_Data *dataOut = elf_newdata(scnOut);
+ if (!dataOut) {
+ ELFU_WARNELF("elf_newdata");
+ }
+
+ dataOut->d_align = ms->data.d_align;
+ dataOut->d_buf = ms->data.d_buf;
+ dataOut->d_off = ms->data.d_off;
+ dataOut->d_type = ms->data.d_type;
+ dataOut->d_size = ms->data.d_size;
+ dataOut->d_version = ms->data.d_version;
+ }
+
+ return NULL;
+}
+
+
+
+
+
+void elfu_mToElf(ElfuElf *me, Elf *e)
+{
+ if (me->symtab) {
+ elfu_mSymtabFlatten(me);
+ }
+
+
+ /* We control the ELF file's layout now. */
+ /* tired's libelf also offers ELF_F_LAYOUT_OVERLAP for overlapping sections,
+ * but we don't want that since we filtered it out in the reading stage
+ * already. It would be too mind-blowing to handle the dependencies between
+ * the PHDRs and sections then... */
+ elf_flagelf(e, ELF_C_SET, ELF_F_LAYOUT);
+
+
+ /* EHDR */
+ if (!gelf_newehdr(e, me->elfclass)) {
+ ELFU_WARNELF("gelf_newehdr");
+ }
+
+ if (me->shstrtab) {
+ me->ehdr.e_shstrndx = elfu_mScnIndex(me, me->shstrtab);
+ }
+
+ if (!gelf_update_ehdr(e, &me->ehdr)) {
+ ELFU_WARNELF("gelf_update_ehdr");
+ }
+
+
+ /* Sections */
+ elfu_mScnForall(me, modelToSection, e, NULL);
+
+
+ /* PHDRs */
+ modelToPhdrs(me, e);
+
+
+ elf_flagelf(e, ELF_C_SET, ELF_F_DIRTY);
+}
+++ /dev/null
-#include <assert.h>
-#include <stdlib.h>
-#include <sys/types.h>
-#include <libelfu/libelfu.h>
-
-
-int elfu_mCheck(ElfuElf *me)
-{
- size_t numSecs;
- ElfuScn **sortedSecs;
- size_t i;
-
- sortedSecs = elfu_mScnSortedByOffset(me, &numSecs);
- if (!sortedSecs) {
- return -1;
- }
-
-
- /* Check for overlapping sections */
- for (i = 0; i < numSecs - 1; i++) {
- if (sortedSecs[i]->shdr.sh_offset + SCNFILESIZE(&sortedSecs[i]->shdr)
- > sortedSecs[i+1]->shdr.sh_offset) {
- ELFU_WARN("elfu_check: Found overlapping sections: %s and %s.\n",
- elfu_mScnName(me, sortedSecs[i]),
- elfu_mScnName(me, sortedSecs[i+1]));
- }
- }
-
-
- /* Check for sections overlapping with EHDR */
- for (i = 0; i < numSecs; i++) {
- if (sortedSecs[i]->shdr.sh_offset < me->ehdr.e_ehsize) {
- ELFU_WARN("elfu_check: Found section overlapping with EHDR: %s.\n",
- elfu_mScnName(me, sortedSecs[i]));
- }
- }
-
-
- /* Check for sections overlapping with PHDRs */
- for (i = 0; i < numSecs; i++) {
- if (OVERLAPPING(sortedSecs[i]->shdr.sh_offset,
- SCNFILESIZE(&sortedSecs[i]->shdr),
- me->ehdr.e_phoff,
- me->ehdr.e_phentsize * me->ehdr.e_phnum)) {
- ELFU_WARN("elfu_check: Found section overlapping with PHDRs: %s.\n",
- elfu_mScnName(me, sortedSecs[i]));
- }
- }
-
-
- free(sortedSecs);
-
- return 0;
-}
+++ /dev/null
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <libelfu/libelfu.h>
-
-ElfuScn* elfu_mCloneScn(ElfuScn *ms)
-{
- ElfuScn *newscn;
-
- assert(ms);
-
- newscn = malloc(sizeof(ElfuScn));
- if (!newscn) {
- ELFU_WARN("elfu_nCloneScn: Could not allocate memory for new ElfuScn.\n");
- return NULL;
- }
-
- newscn->shdr = ms->shdr;
- newscn->data = ms->data;
- if (ms->data.d_buf) {
- void *newbuf = malloc(ms->data.d_size);
- if (!newbuf) {
- ELFU_WARN("elfu_nCloneScn: Could not allocate memory for new data buffer.\n");
- free(newscn);
- return NULL;
- }
-
- memcpy(newbuf, ms->data.d_buf, ms->data.d_size);
- newscn->data.d_buf = newbuf;
- }
-
- newscn->linkptr = NULL;
- newscn->infoptr = NULL;
-
- newscn->oldptr = ms;
-
- CIRCLEQ_INIT(&ms->symtab.syms);
- CIRCLEQ_INIT(&ms->reltab.rels);
-
- return newscn;
-}
+++ /dev/null
-#include <libelfu/libelfu.h>
-#include <string.h>
-
-static void* subFindByAddr(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
-{
- GElf_Addr a = *(GElf_Addr*)aux1;
-
- if (OVERLAPPING(ms->shdr.sh_addr, ms->shdr.sh_size, a, 1)) {
- return ms;
- }
-
- /* Continue */
- return NULL;
-}
-
-
-void elfu_mDetour(ElfuElf *me, GElf_Addr from, GElf_Addr to)
-{
- ElfuScn *ms;
- GElf_Word scnoffset;
- unsigned char detourcode[] = {0xe9, 0xfc, 0xff, 0xff, 0xff};
-
- ms = elfu_mScnForall(me, subFindByAddr, &from, NULL);
-
- if (!ms) {
- ELFU_WARN("mDetour: Cannot find address %x in any section.\n",
- (unsigned)from);
- return;
- }
-
- if (ms->shdr.sh_type != SHT_PROGBITS) {
- ELFU_WARN("mDetour: Cannot detour in non-PROGBITS section %s.\n",
- elfu_mScnName(me, ms));
- return;
- }
-
- scnoffset = from - ms->shdr.sh_addr;
-
- if (ms->shdr.sh_size - scnoffset < 5) {
- ELFU_WARN("mDetour: Not enough space to insert a detour.\n");
- return;
- }
-
- ELFU_DEBUG("mDetour: Detouring at address %x in section %s to %x.\n",
- (unsigned)from,
- elfu_mScnName(me, ms),
- (unsigned)to);
-
- *(Elf32_Word*)(detourcode + 1) = to - from - 5;
- memcpy((char*)ms->data.d_buf + scnoffset, detourcode, 5);
-}
+++ /dev/null
-#include <assert.h>
-#include <libelfu/libelfu.h>
-
-
-static char* segmentTypeStr(Elf32_Word p_type)
-{
- switch(p_type) {
- case PT_NULL: /* 0 */
- return "NULL";
- case PT_LOAD: /* 1 */
- return "LOAD";
- case PT_DYNAMIC: /* 2 */
- return "DYNAMIC";
- case PT_INTERP: /* 3 */
- return "INTERP";
- case PT_NOTE: /* 4 */
- return "NOTE";
- case PT_SHLIB: /* 5 */
- return "SHLIB";
- case PT_PHDR: /* 6 */
- return "PHDR";
- case PT_TLS: /* 7 */
- return "TLS";
- case PT_NUM: /* 8 */
- return "NUM";
- case PT_GNU_EH_FRAME: /* 0x6474e550 */
- return "GNU_EH_FRAME";
- case PT_GNU_STACK: /* 0x6474e551 */
- return "GNU_STACK";
- case PT_GNU_RELRO: /* 0x6474e552 */
- return "GNU_RELRO";
- }
-
- return "-?-";
-}
-
-static char* sectionTypeStr(Elf32_Word sh_type)
-{
- switch(sh_type) {
- case SHT_NULL: /* 0 */
- return "NULL";
- case SHT_PROGBITS: /* 1 */
- return "PROGBITS";
- case SHT_SYMTAB: /* 2 */
- return "SYMTAB";
- case SHT_STRTAB: /* 3 */
- return "STRTAB";
- case SHT_RELA: /* 4 */
- return "RELA";
- case SHT_HASH: /* 5 */
- return "HASH";
- case SHT_DYNAMIC: /* 6 */
- return "DYNAMIC";
- case SHT_NOTE: /* 7 */
- return "NOTE";
- case SHT_NOBITS: /* 8 */
- return "NOBITS";
- case SHT_REL: /* 9 */
- return "REL";
- case SHT_SHLIB: /* 10 */
- return "SHLIB";
- case SHT_DYNSYM: /* 11 */
- return "DYNSYM";
- case SHT_INIT_ARRAY: /* 14 */
- return "INIT_ARRAY";
- case SHT_FINI_ARRAY: /* 15 */
- return "FINI_ARRAY";
- case SHT_PREINIT_ARRAY: /* 16 */
- return "PREINIT_ARRAY";
- case SHT_GROUP: /* 17 */
- return "SHT_GROUP";
- case SHT_SYMTAB_SHNDX: /* 18 */
- return "SYMTAB_SHNDX";
- case SHT_NUM: /* 19 */
- return "NUM";
-
- case SHT_GNU_ATTRIBUTES: /* 0x6ffffff5 */
- return "GNU_ATTRIBUTES";
- case SHT_GNU_HASH: /* 0x6ffffff6 */
- return "GNU_HASH";
- case SHT_GNU_LIBLIST: /* 0x6ffffff7 */
- return "GNU_LIBLIST";
- case SHT_GNU_verdef: /* 0x6ffffffd */
- return "GNU_verdef";
- case SHT_GNU_verneed: /* 0x6ffffffe */
- return "GNU_verneed";
- case SHT_GNU_versym: /* 0x6fffffff */
- return "GNU_versym";
- }
-
- return "-?-";
-}
-
-
-
-
-void elfu_mDumpPhdr(ElfuElf *me, ElfuPhdr *mp)
-{
- assert(me);
- assert(mp);
-
- ELFU_INFO("%12s %8x %8x %8x %8x %8x %8x %8x %8x\n",
- segmentTypeStr(mp->phdr.p_type),
- (unsigned)mp->phdr.p_type,
- (unsigned)mp->phdr.p_offset,
- (unsigned)mp->phdr.p_vaddr,
- (unsigned)mp->phdr.p_paddr,
- (unsigned)mp->phdr.p_filesz,
- (unsigned)mp->phdr.p_memsz,
- (unsigned)mp->phdr.p_flags,
- (unsigned)mp->phdr.p_align);
-
- if (!CIRCLEQ_EMPTY(&mp->childPhdrList)) {
- ElfuPhdr *mpc;
-
- ELFU_INFO(" -> Child PHDRs:\n");
- CIRCLEQ_FOREACH(mpc, &mp->childPhdrList, elemChildPhdr) {
- ELFU_INFO(" * %-8s @ %8x\n",
- segmentTypeStr(mpc->phdr.p_type),
- (unsigned)mpc->phdr.p_vaddr);
- }
- }
-
- if (!CIRCLEQ_EMPTY(&mp->childScnList)) {
- ElfuScn *msc;
-
- ELFU_INFO(" -> Child sections:\n");
- CIRCLEQ_FOREACH(msc, &mp->childScnList, elemChildScn) {
- ELFU_INFO(" * %-17s @ %8x\n",
- elfu_mScnName(me, msc),
- (unsigned)msc->shdr.sh_addr);
- }
- }
-}
-
-
-void elfu_mDumpScn(ElfuElf *me, ElfuScn *ms)
-{
- char *namestr, *typestr, *linkstr, *infostr;
-
- assert(me);
- assert(ms);
-
- namestr = elfu_mScnName(me, ms);
- typestr = sectionTypeStr(ms->shdr.sh_type);
- linkstr = ms->linkptr ? elfu_mScnName(me, ms->linkptr) : "";
- infostr = ms->infoptr ? elfu_mScnName(me, ms->infoptr) : "";
-
- ELFU_INFO("%-17s %-15s %8x %9x %8x %2x %2x %2d %-17s %-17s\n",
- namestr,
- typestr,
- (unsigned)ms->shdr.sh_addr,
- (unsigned)ms->shdr.sh_offset,
- (unsigned)ms->shdr.sh_size,
- (unsigned)ms->shdr.sh_entsize,
- (unsigned)ms->shdr.sh_flags,
- (unsigned)ms->shdr.sh_addralign,
- linkstr,
- infostr);
-}
-
-
-void elfu_mDumpEhdr(ElfuElf *me)
-{
- assert(me);
-
- ELFU_INFO("ELF header:\n");
- ELFU_INFO(" %d-bit ELF object\n", me->elfclass == ELFCLASS32 ? 32 : 64);
-
- ELFU_INFO(" EHDR:\n");
-
- ELFU_INFO(" e_type %8x\n", me->ehdr.e_type);
- ELFU_INFO(" e_machine %8x\n", me->ehdr.e_machine);
- ELFU_INFO(" e_version %8x\n", me->ehdr.e_version);
- ELFU_INFO(" e_entry %8x\n", (unsigned)me->ehdr.e_entry);
- ELFU_INFO(" e_phoff %8x\n", (unsigned)me->ehdr.e_phoff);
- ELFU_INFO(" e_shoff %8x\n", (unsigned)me->ehdr.e_shoff);
- ELFU_INFO(" e_flags %8x\n", me->ehdr.e_flags);
- ELFU_INFO(" e_ehsize %8x\n", me->ehdr.e_ehsize);
- ELFU_INFO(" e_phentsize %8x\n", me->ehdr.e_phentsize);
- ELFU_INFO(" e_shentsize %8x\n", me->ehdr.e_shentsize);
-
- ELFU_INFO(" shstrtab: %s\n", me->shstrtab ? elfu_mScnName(me, me->shstrtab) : "(none)");
-}
-
-
-
-static void* subScnDump(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
-{
- (void) aux1;
- (void) aux2;
-
- printf(" [%4d] ", elfu_mScnIndex(me, ms));
- elfu_mDumpScn(me, ms);
-
- return NULL;
-}
-
-
-void elfu_mDumpElf(ElfuElf *me)
-{
- ElfuPhdr *mp;
- ElfuScn *ms;
- size_t i;
-
- assert(me);
-
-
- elfu_mDumpEhdr(me);
- ELFU_INFO("\n");
-
-
- ELFU_INFO("Segments:\n");
- ELFU_INFO(" # (type) p_type p_offset p_vaddr p_paddr p_filesz p_memsz p_flags p_align\n");
- ELFU_INFO(" | | | | | | | | | \n");
- i = 0;
- CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
- printf(" [%4d] ", i);
- elfu_mDumpPhdr(me, mp);
- i++;
- }
- ELFU_INFO("\n");
-
-
- ELFU_INFO("Orphaned sections:\n");
- CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
- ELFU_INFO(" * %-17s @ %8x\n",
- elfu_mScnName(me, ms),
- (unsigned)ms->shdr.sh_addr);
- }
- ELFU_INFO("\n");
-
-
- ELFU_INFO("Sections:\n");
- ELFU_INFO(" # Name sh_type sh_addr sh_offset sh_size ES Fl Al sh_link sh_info \n");
- ELFU_INFO(" | | | | | | | | | | \n");
- elfu_mScnForall(me, subScnDump, &i, NULL);
- ELFU_INFO("\n");
-}
+++ /dev/null
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <libelfu/libelfu.h>
-
-
-static void parseSymtab(ElfuElf *me, ElfuScn *ms, ElfuScn**origScnArr)
-{
- ElfuSym *sym;
- size_t i;
-
- assert(ms);
- assert(ms->data.d_buf);
- assert(origScnArr);
-
- /* Parse symbols from their elfclass-specific format */
- if (me->elfclass == ELFCLASS32) {
- for (i = 1; (i + 1) * sizeof(Elf32_Sym) <= ms->shdr.sh_size; i++) {
- Elf32_Sym *cursym = ((Elf32_Sym*)ms->data.d_buf) + i;
- ElfuSym *newsym = malloc(sizeof(*sym));
- assert(newsym);
-
- newsym->name = cursym->st_name;
- newsym->value = cursym->st_value;
- newsym->size = cursym->st_size;
- newsym->bind = ELF32_ST_BIND(cursym->st_info);
- newsym->type = ELF32_ST_TYPE(cursym->st_info);
- newsym->other = cursym->st_other;
- newsym->shndx = cursym->st_shndx;
-
-
-
- CIRCLEQ_INSERT_TAIL(&ms->symtab.syms, newsym, elem);
- }
- } else if (me->elfclass == ELFCLASS64) {
- for (i = 1; (i + 1) * sizeof(Elf64_Sym) <= ms->shdr.sh_size; i++) {
- Elf64_Sym *cursym = ((Elf64_Sym*)ms->data.d_buf) + i;
- ElfuSym *newsym = malloc(sizeof(*sym));
- assert(newsym);
-
- newsym->name = cursym->st_name;
- newsym->value = cursym->st_value;
- newsym->size = cursym->st_size;
- newsym->bind = ELF64_ST_BIND(cursym->st_info);
- newsym->type = ELF64_ST_TYPE(cursym->st_info);
- newsym->other = cursym->st_other;
- newsym->shndx = cursym->st_shndx;
-
-
-
- CIRCLEQ_INSERT_TAIL(&ms->symtab.syms, newsym, elem);
- }
- } else {
- /* Unknown elfclass */
- assert(0);
- }
-
- /* For each section, find the section it points to if any. */
- CIRCLEQ_FOREACH(sym, &ms->symtab.syms, elem) {
- switch (sym->shndx) {
- case SHN_UNDEF:
- case SHN_ABS:
- case SHN_COMMON:
- sym->scnptr = NULL;
- break;
- default:
- sym->scnptr = origScnArr[sym->shndx - 1];
- break;
- }
- }
-}
-
-
-static void parseReltab32(ElfuScn *ms)
-{
- size_t i;
-
- assert(ms);
- assert(ms->data.d_buf);
-
-
- for (i = 0; (i + 1) * sizeof(Elf32_Rel) <= ms->shdr.sh_size; i++) {
- Elf32_Rel *currel = &(((Elf32_Rel*)ms->data.d_buf)[i]);
- ElfuRel *rel;
-
- rel = malloc(sizeof(*rel));
- assert(rel);
-
- rel->offset = currel->r_offset;
- rel->sym = ELF32_R_SYM(currel->r_info);
- rel->type = ELF32_R_TYPE(currel->r_info);
- rel->addendUsed = 0;
- rel->addend = 0;
-
- CIRCLEQ_INSERT_TAIL(&ms->reltab.rels, rel, elem);
- }
-}
-
-
-static void parseRelatab64(ElfuScn *ms)
-{
- size_t i;
-
- assert(ms);
- assert(ms->data.d_buf);
-
-
- for (i = 0; (i + 1) * sizeof(Elf64_Rela) <= ms->shdr.sh_size; i++) {
- Elf64_Rela *currel = &(((Elf64_Rela*)ms->data.d_buf)[i]);
- ElfuRel *rel;
-
- rel = malloc(sizeof(*rel));
- assert(rel);
-
- rel->offset = currel->r_offset;
- rel->sym = ELF64_R_SYM(currel->r_info);
- rel->type = ELF64_R_TYPE(currel->r_info);
- rel->addendUsed = 1;
- rel->addend = currel->r_addend;
-
- CIRCLEQ_INSERT_TAIL(&ms->reltab.rels, rel, elem);
- }
-}
-
-
-static int cmpScnOffs(const void *ms1, const void *ms2)
-{
- ElfuScn *s1 = *(ElfuScn**)ms1;
- ElfuScn *s2 = *(ElfuScn**)ms2;
-
- assert(ms1);
- assert(ms2);
-
- s1 = *(ElfuScn**)ms1;
- s2 = *(ElfuScn**)ms2;
-
- assert(s1);
- assert(s2);
-
-
- if (s1->shdr.sh_offset < s2->shdr.sh_offset) {
- return -1;
- } else if (s1->shdr.sh_offset == s2->shdr.sh_offset) {
- return 0;
- } else /* if (s1->shdr.sh_offset > s2->shdr.sh_offset) */ {
- return 1;
- }
-}
-
-
-
-static ElfuPhdr* parentPhdr(ElfuElf *me, ElfuScn *ms)
-{
- ElfuPhdr *mp;
-
- assert(me);
- assert(ms);
-
- CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
- if (mp->phdr.p_type != PT_LOAD) {
- continue;
- }
-
- if (PHDR_CONTAINS_SCN_IN_MEMORY(&mp->phdr, &ms->shdr)) {
- return mp;
- }
-
- /* Give sections a second chance if they do not have any sh_addr
- * at all. */
- /* Actually we don't, because it's ambiguous.
- * Re-enable for experiments with strangely-formatted files.
- if (ms->shdr.sh_addr == 0
- && PHDR_CONTAINS_SCN_IN_FILE(&mp->phdr, &ms->shdr)
- && OFFS_END(ms->shdr.sh_offset, ms->shdr.sh_size)
- <= OFFS_END(mp->phdr.p_offset, mp->phdr.p_memsz)) {
- return mp;
- }
- */
- }
-
- return NULL;
-}
-
-
-static ElfuPhdr* modelFromPhdr(GElf_Phdr *phdr)
-{
- ElfuPhdr *mp;
-
- assert(phdr);
-
- mp = malloc(sizeof(ElfuPhdr));
- if (!mp) {
- ELFU_WARN("modelFromPhdr: malloc() failed for ElfuPhdr.\n");
- return NULL;
- }
-
- mp->phdr = *phdr;
-
- CIRCLEQ_INIT(&mp->childScnList);
- CIRCLEQ_INIT(&mp->childPhdrList);
-
- return mp;
-}
-
-
-static ElfuScn* modelFromSection(Elf_Scn *scn)
-{
- ElfuScn *ms;
-
- assert(scn);
-
- ms = malloc(sizeof(ElfuScn));
- if (!ms) {
- ELFU_WARN("modelFromSection: malloc() failed for ElfuScn.\n");
- goto ERROR;
- }
-
-
- assert(gelf_getshdr(scn, &ms->shdr) == &ms->shdr);
-
-
- /* Copy each data part in source segment */
- ms->data.d_align = 1;
- ms->data.d_buf = NULL;
- ms->data.d_off = 0;
- ms->data.d_type = ELF_T_BYTE;
- ms->data.d_size = ms->shdr.sh_size;
- ms->data.d_version = elf_version(EV_NONE);
- if (ms->shdr.sh_type != SHT_NOBITS
- && ms->shdr.sh_size > 0) {
- Elf_Data *data;
-
- ms->data.d_buf = malloc(ms->shdr.sh_size);
- if (!ms->data.d_buf) {
- ELFU_WARN("modelFromSection: malloc() failed for data buffer (%x bytes).\n", (unsigned)ms->shdr.sh_size);
- goto ERROR;
- }
-
- /* A non-empty section should contain at least one data block. */
- data = elf_rawdata(scn, NULL);
- assert(data);
-
- ms->data.d_align = data->d_align;
- ms->data.d_type = data->d_type;
- ms->data.d_version = data->d_version;
-
- while (data) {
- if (data->d_off + data->d_size > ms->shdr.sh_size) {
- ELFU_WARN("modelFromSection: libelf delivered a bogus data blob. Skipping\n");
- } else {
- memcpy((char*)ms->data.d_buf + data->d_off, data->d_buf, data->d_size);
- }
-
- data = elf_rawdata(scn, data);
- }
- }
-
- ms->linkptr = NULL;
- ms->infoptr = NULL;
-
- ms->oldptr = NULL;
-
- CIRCLEQ_INIT(&ms->symtab.syms);
- CIRCLEQ_INIT(&ms->reltab.rels);
-
-
- return ms;
-
- ERROR:
- if (ms) {
- free(ms);
- }
- return NULL;
-}
-
-
-
-
-ElfuElf* elfu_mFromElf(Elf *e)
-{
- ElfuElf *me;
- size_t shstrndx;
- size_t i, numPhdr, numShdr;
- ElfuScn **secArray = NULL;
-
- assert(e);
- if (elfu_eCheck(e)) {
- goto ERROR;
- }
-
- me = malloc(sizeof(ElfuElf));
- if (!me) {
- ELFU_WARN("elfu_mFromElf: malloc() failed for ElfuElf.\n");
- goto ERROR;
- }
-
-
- /* General stuff */
- CIRCLEQ_INIT(&me->phdrList);
- CIRCLEQ_INIT(&me->orphanScnList);
- me->shstrtab = NULL;
- me->symtab = NULL;
-
- me->elfclass = gelf_getclass(e);
- assert(me->elfclass != ELFCLASSNONE);
- assert(gelf_getehdr(e, &me->ehdr) == &me->ehdr);
-
-
- /* Get the section string table index */
- if (elf_getshdrstrndx(e, &shstrndx) != 0) {
- shstrndx = 0;
- }
-
-
- /* Load segments */
- assert(!elf_getphdrnum(e, &numPhdr));
- for (i = 0; i < numPhdr; i++) {
- GElf_Phdr phdr;
- ElfuPhdr *mp;
-
- assert(gelf_getphdr(e, i, &phdr) == &phdr);
-
- mp = modelFromPhdr(&phdr);
- if (!mp) {
- goto ERROR;
- }
-
- CIRCLEQ_INSERT_TAIL(&me->phdrList, mp, elem);
- }
-
- if (numPhdr > 0) {
- ElfuPhdr *mp;
-
- /* Find PHDR -> PHDR dependencies (needs sorted sections) */
- CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
- ElfuPhdr *mp2;
-
- if (mp->phdr.p_type != PT_LOAD) {
- continue;
- }
-
- CIRCLEQ_FOREACH(mp2, &me->phdrList, elem) {
- if (mp2 == mp) {
- continue;
- }
-
- if (mp->phdr.p_vaddr <= mp2->phdr.p_vaddr
- && OFFS_END(mp2->phdr.p_vaddr, mp2->phdr.p_memsz) <= OFFS_END(mp->phdr.p_vaddr, mp->phdr.p_memsz)) {
- CIRCLEQ_INSERT_TAIL(&mp->childPhdrList, mp2, elemChildPhdr);
- }
- }
- }
- }
-
-
- /* Load sections */
- assert(!elf_getshdrnum(e, &numShdr));
- if (numShdr > 1) {
- secArray = malloc((numShdr - 1) * sizeof(*secArray));
- if (!secArray) {
- ELFU_WARN("elfu_mFromElf: malloc() failed for secArray.\n");
- goto ERROR;
- }
-
- for (i = 1; i < numShdr; i++) {
- Elf_Scn *scn;
- ElfuScn *ms;
-
- scn = elf_getscn(e, i);
- assert(scn);
-
- ms = modelFromSection(scn);
- if (!ms) {
- goto ERROR;
- }
-
- secArray[i-1] = ms;
-
- if (i == shstrndx) {
- me->shstrtab = ms;
- }
- }
-
-
- /* Find sh_link and sh_info dependencies (needs sections in original order) */
- for (i = 0; i < numShdr - 1; i++) {
- ElfuScn *ms = secArray[i];
-
- switch (ms->shdr.sh_type) {
- case SHT_REL:
- case SHT_RELA:
- if (ms->shdr.sh_info > 0) {
- ms->infoptr = secArray[ms->shdr.sh_info - 1];
- }
- case SHT_DYNAMIC:
- case SHT_HASH:
- case SHT_SYMTAB:
- case SHT_DYNSYM:
- case SHT_GNU_versym:
- case SHT_GNU_verdef:
- case SHT_GNU_verneed:
- if (ms->shdr.sh_link > 0) {
- ms->linkptr = secArray[ms->shdr.sh_link - 1];
- }
- }
- }
-
-
- /* Parse symtabs (needs sections in original order) */
- for (i = 0; i < numShdr - 1; i++) {
- ElfuScn *ms = secArray[i];
-
- switch (ms->shdr.sh_type) {
- case SHT_SYMTAB:
- me->symtab = ms;
- case SHT_DYNSYM:
- parseSymtab(me, ms, secArray);
- break;
- }
- }
-
-
- /* Parse relocations */
- for (i = 0; i < numShdr - 1; i++) {
- ElfuScn *ms = secArray[i];
-
- switch (ms->shdr.sh_type) {
- case SHT_REL:
- if (me->elfclass == ELFCLASS32) {
- parseReltab32(ms);
- } else if (me->elfclass == ELFCLASS64) {
- /* Not used on x86-64 */
- assert(0);
- }
- break;
- case SHT_RELA:
- if (me->elfclass == ELFCLASS32) {
- /* Not used on x86-32 */
- assert(0);
- } else if (me->elfclass == ELFCLASS64) {
- parseRelatab64(ms);
- }
- break;
- }
- }
-
-
- /* Sort sections by file offset */
- qsort(secArray, numShdr - 1, sizeof(*secArray), cmpScnOffs);
-
-
- /* Find PHDR -> Section dependencies (needs sorted sections) */
- for (i = 0; i < numShdr - 1; i++) {
- ElfuScn *ms = secArray[i];
-
- ElfuPhdr *parent = parentPhdr(me, ms);
-
- if (parent) {
- GElf_Off shaddr = parent->phdr.p_vaddr +
- (ms->shdr.sh_offset - parent->phdr.p_offset);
-
- if (ms->shdr.sh_addr == 0) {
- ms->shdr.sh_addr = shaddr;
- } else {
- assert(ms->shdr.sh_addr == shaddr);
- }
-
- CIRCLEQ_INSERT_TAIL(&parent->childScnList, ms, elemChildScn);
- } else {
- CIRCLEQ_INSERT_TAIL(&me->orphanScnList, ms, elemChildScn);
- }
- }
- }
-
-
- return me;
-
-
- ERROR:
- if (secArray) {
- free(secArray);
- }
- if (me) {
- // TODO: Free data structures
- }
-
- ELFU_WARN("elfu_mFromElf: Failed to load file.\n");
- return NULL;
-}
+++ /dev/null
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <libelfu/libelfu.h>
-
-
-
-static GElf_Word shiftStuffAtAfterOffset(ElfuElf *me,
- GElf_Off offset,
- GElf_Word size)
-{
- ElfuPhdr *mp;
- ElfuScn *ms;
- /* Force a minimum alignment, just to be sure. */
- GElf_Word align = 64;
-
- /* Find maximum alignment size by which we have to shift.
- * Assumes alignment sizes are always 2^x. */
- CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
- if (mp->phdr.p_offset >= offset) {
- if (mp->phdr.p_align > align) {
- align = mp->phdr.p_align;
- }
- }
- }
-
- CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
- if (ms->shdr.sh_offset >= offset) {
- if (ms->shdr.sh_addralign > align) {
- align = ms->shdr.sh_addralign;
- }
- }
- }
-
- size = ROUNDUP(size, align);
-
- /* Shift stuff */
- CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
- if (mp->phdr.p_type != PT_LOAD) {
- continue;
- }
-
- if (mp->phdr.p_offset >= offset) {
- mp->phdr.p_offset += size;
-
- elfu_mPhdrUpdateChildOffsets(mp);
- }
- }
-
- CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
- if (ms->shdr.sh_offset >= offset) {
- ms->shdr.sh_offset += size;
- }
- }
-
- if (me->ehdr.e_phoff >= offset) {
- me->ehdr.e_phoff += size;
- }
-
- if (me->ehdr.e_shoff >= offset) {
- me->ehdr.e_shoff += size;
- }
-
- return size;
-}
-
-
-
-
-
-/* Finds a suitable PHDR to insert a hole into and expands it
- * if necessary.
- * Returns memory address the hole will be mapped to, or 0 if
- * the operation failed. */
-GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
- GElf_Word align, int w, int x,
- ElfuPhdr **injPhdr)
-{
- ElfuPhdr *first = NULL;
- ElfuPhdr *last = NULL;
- ElfuPhdr *mp;
-
- assert(!(w && x));
-
- /* Treat read-only data as executable.
- * That's what the GNU toolchain does on x86. */
- if (!w && !x) {
- x = 1;
- }
-
- /* Find first and last LOAD PHDRs.
- * Don't compare p_memsz - segments don't overlap in memory. */
- CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
- if (mp->phdr.p_type != PT_LOAD) {
- continue;
- }
- if (!first || mp->phdr.p_vaddr < first->phdr.p_vaddr) {
- first = mp;
- }
- if (!last || mp->phdr.p_vaddr > last->phdr.p_vaddr) {
- last = mp;
- }
- }
-
- if ((w && (last->phdr.p_flags & PF_W))
- || (x && (last->phdr.p_flags & PF_X))) {
- /* Need to append. */
- GElf_Off injOffset = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz);
- GElf_Word injSpace = 0;
- GElf_Word nobitsize = last->phdr.p_memsz - last->phdr.p_filesz;
-
- /* Expand NOBITS if any */
- if (nobitsize > 0) {
- GElf_Off endOff = OFFS_END(last->phdr.p_offset, last->phdr.p_filesz);
- GElf_Off endAddr = OFFS_END(last->phdr.p_vaddr, last->phdr.p_filesz);
- ElfuScn *ms;
-
- ELFU_INFO("Expanding NOBITS at address 0x%x...\n", (unsigned)endAddr);
-
- CIRCLEQ_FOREACH(ms, &last->childScnList, elemChildScn) {
- if (ms->shdr.sh_offset == endOff) {
- assert(ms->shdr.sh_type == SHT_NOBITS);
- assert(ms->shdr.sh_size == nobitsize);
- ms->data.d_buf = malloc(ms->shdr.sh_size);
- memset(ms->data.d_buf, '\0', ms->shdr.sh_size);
- if (!ms->data.d_buf) {
- ELFU_WARN("mExpandNobits: Could not allocate %u bytes for NOBITS expansion. Data may be inconsistent.\n",
- (unsigned)ms->shdr.sh_size);
- assert(0);
- goto ERROR;
- }
-
- ms->data.d_align = 1;
- ms->data.d_off = 0;
- ms->data.d_type = ELF_T_BYTE;
- ms->data.d_size = ms->shdr.sh_size;
- ms->data.d_version = elf_version(EV_NONE);
-
- ms->shdr.sh_type = SHT_PROGBITS;
- ms->shdr.sh_addr = endAddr;
- }
- }
-
- injSpace += shiftStuffAtAfterOffset(me, endOff, nobitsize);
- injSpace -= nobitsize;
- injOffset += nobitsize;
- last->phdr.p_filesz += nobitsize;
- assert(last->phdr.p_filesz == last->phdr.p_memsz);
- }
-
- /* Calculate how much space we need, taking alignment into account */
- size += ROUNDUP(injOffset, align) - injOffset;
-
- /* If there is not enough space left, create even more. */
- if (injSpace < size) {
- injSpace += shiftStuffAtAfterOffset(me, injOffset, size - injSpace);
- }
- assert(injSpace >= size);
-
- /* Remap ourselves */
- last->phdr.p_filesz += size;
- last->phdr.p_memsz += size;
-
- injOffset = ROUNDUP(injOffset, align);
-
- if (injPhdr) {
- *injPhdr = last;
- }
- return last->phdr.p_vaddr + (injOffset - last->phdr.p_offset);
- } else if ((w && (first->phdr.p_flags & PF_W))
- || (x && (first->phdr.p_flags & PF_X))) {
- /* Need to prepend or split up the PHDR. */
- GElf_Off injOffset = OFFS_END(first->phdr.p_offset, first->phdr.p_filesz);
- ElfuScn *ms;
-
- /* Round up size to take PHDR alignment into account.
- * We assume that this is a multiple of the alignment asked for. */
- assert(first->phdr.p_align >= align);
- size = ROUNDUP(size, first->phdr.p_align);
-
- /* Find first section. We assume there is at least one. */
- assert(!CIRCLEQ_EMPTY(&first->childScnList));
- injOffset = CIRCLEQ_FIRST(&first->childScnList)->shdr.sh_offset;
-
- /* Move our sections */
- CIRCLEQ_FOREACH(ms, &first->childScnList, elemChildScn) {
- if (ms->shdr.sh_offset >= injOffset) {
- ms->shdr.sh_offset += size;
- }
- }
-
- /* Move our PHDRs */
- CIRCLEQ_FOREACH(mp, &first->childPhdrList, elemChildPhdr) {
- if (mp->phdr.p_offset >= injOffset) {
- mp->phdr.p_offset += size;
- } else {
- mp->phdr.p_vaddr -= size;
- mp->phdr.p_paddr -= size;
- }
- }
-
- /* Move other PHDRs and sections */
- assert(size <= shiftStuffAtAfterOffset(me, injOffset, size));
-
- /* Remap ourselves */
- first->phdr.p_vaddr -= size;
- first->phdr.p_paddr -= size;
- first->phdr.p_filesz += size;
- first->phdr.p_memsz += size;
-
- injOffset = ROUNDUP(injOffset, align);
-
- if (injPhdr) {
- *injPhdr = first;
- }
- return first->phdr.p_vaddr + (injOffset - first->phdr.p_offset);
- }
-
- ERROR:
- if (injPhdr) {
- *injPhdr = NULL;
- }
- return 0;
-}
-
-
-
-
-static int cmpPhdrOffs(const void *mp1, const void *mp2)
-{
- ElfuPhdr *p1;
- ElfuPhdr *p2;
-
- assert(mp1);
- assert(mp2);
-
- p1 = *(ElfuPhdr**)mp1;
- p2 = *(ElfuPhdr**)mp2;
-
- assert(p1);
- assert(p2);
-
-
- if (p1->phdr.p_offset < p2->phdr.p_offset) {
- return -1;
- } else if (p1->phdr.p_offset == p2->phdr.p_offset) {
- return 0;
- } else /* if (p1->phdr.p_offset > p2->phdr.p_offset) */ {
- return 1;
- }
-}
-
-int elfu_mLayoutAuto(ElfuElf *me)
-{
- ElfuPhdr *mp;
- ElfuScn *ms;
- ElfuPhdr **phdrArr;
- GElf_Off lastend = 0;
- size_t i, j;
-
- assert(me);
-
- phdrArr = malloc(elfu_mPhdrCount(me) * sizeof(*phdrArr));
- if (!phdrArr) {
- ELFU_WARN("elfu_mLayoutAuto: malloc failed for phdrArr.\n");
- return 1;
- }
-
- i = 0;
- CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
- if (mp->phdr.p_type != PT_LOAD) {
- continue;
- }
-
- phdrArr[i] = mp;
- i++;
- }
-
- /* Assume we have at least one LOAD PHDR,
- * and that it ends after EHDR and PHDRs */
- assert(i > 1);
-
- /* Sort array by file offset */
- qsort(phdrArr, i, sizeof(*phdrArr), cmpPhdrOffs);
-
- lastend = OFFS_END(phdrArr[0]->phdr.p_offset, phdrArr[0]->phdr.p_filesz);
-
- /* Wiggle offsets of 2nd, 3rd etc so take minimum space */
- for (j = 1; j < i; j++) {
- GElf_Off subalign = phdrArr[j]->phdr.p_offset % phdrArr[j]->phdr.p_align;
-
- if ((lastend % phdrArr[j]->phdr.p_align) <= subalign) {
- lastend += subalign - (lastend % phdrArr[j]->phdr.p_align);
- } else {
- lastend += phdrArr[j]->phdr.p_align - ((lastend % phdrArr[j]->phdr.p_align) - subalign);
- }
-
- phdrArr[j]->phdr.p_offset = lastend;
-
- elfu_mPhdrUpdateChildOffsets(phdrArr[j]);
-
- lastend = OFFS_END(phdrArr[j]->phdr.p_offset, phdrArr[j]->phdr.p_filesz);
- }
-
- free(phdrArr);
-
-
- /* Place orphaned sections afterwards, maintaining alignment */
- CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
- lastend = ROUNDUP(lastend, ms->shdr.sh_addralign);
-
- ms->shdr.sh_offset = lastend;
-
- lastend = OFFS_END(ms->shdr.sh_offset, SCNFILESIZE(&ms->shdr));
- }
-
-
- /* Move SHDRs to end */
- lastend = ROUNDUP(lastend, 8);
- me->ehdr.e_shoff = lastend;
-
-
- return 0;
-}
+++ /dev/null
-#include <assert.h>
-#include <libelfu/libelfu.h>
-
-
-size_t elfu_mPhdrCount(ElfuElf *me)
-{
- ElfuPhdr *mp;
- size_t i = 0;
-
- assert(me);
-
- CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
- i++;
- }
-
- return i;
-}
-
-
-
-void elfu_mPhdrUpdateChildOffsets(ElfuPhdr *mp)
-{
- ElfuScn *ms;
- ElfuPhdr *mpc;
-
- assert(mp);
- assert(mp->phdr.p_type == PT_LOAD);
-
- CIRCLEQ_FOREACH(mpc, &mp->childPhdrList, elemChildPhdr) {
- mpc->phdr.p_offset = mp->phdr.p_offset + (mpc->phdr.p_vaddr - mp->phdr.p_vaddr);
- }
-
- CIRCLEQ_FOREACH(ms, &mp->childScnList, elemChildScn) {
- ms->shdr.sh_offset = mp->phdr.p_offset + (ms->shdr.sh_addr - mp->phdr.p_vaddr);
- }
-}
+++ /dev/null
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/types.h>
-#include <libelfu/libelfu.h>
-
-
-static int appendData(ElfuScn *ms, void *buf, size_t len)
-{
- char *newbuf;
-
- assert(ms);
- assert(ms->shdr.sh_type != SHT_NOBITS);
- assert(ms->data.d_buf);
-
- newbuf = realloc(ms->data.d_buf, ms->shdr.sh_size + len);
- if (!newbuf) {
- ELFU_WARN("appendData: malloc() failed for newbuf.\n");
- return 1;
- }
-
- ms->data.d_buf = newbuf;
- memcpy(newbuf + ms->shdr.sh_size, buf, len);
- ms->shdr.sh_size += len;
- ms->data.d_size += len;
- assert(ms->shdr.sh_size == ms->data.d_size);
-
- return 0;
-}
-
-
-static ElfuScn* insertSection(ElfuElf *me, ElfuElf *mrel, ElfuScn *oldscn)
-{
- ElfuScn *newscn = NULL;
- GElf_Addr injAddr;
- GElf_Off injOffset;
- ElfuPhdr *injPhdr;
-
- if (oldscn->shdr.sh_flags & SHF_ALLOC) {
- newscn = elfu_mCloneScn(oldscn);
- if (!newscn) {
- return NULL;
- }
-
- if (newscn->shdr.sh_type == SHT_NOBITS) {
- /* Expand this to SHT_PROGBITS, then insert as such. */
-
- assert(!newscn->data.d_buf);
-
- newscn->data.d_buf = malloc(newscn->shdr.sh_size);
- if (!newscn->data.d_buf) {
- goto ERROR;
- }
- newscn->data.d_size = newscn->shdr.sh_size;
- newscn->shdr.sh_type = SHT_PROGBITS;
- }
-
- injAddr = elfu_mLayoutGetSpaceInPhdr(me,
- newscn->shdr.sh_size,
- newscn->shdr.sh_addralign,
- newscn->shdr.sh_flags & SHF_WRITE,
- newscn->shdr.sh_flags & SHF_EXECINSTR,
- &injPhdr);
-
- if (!injPhdr) {
- ELFU_WARN("insertSection: Could not find a place to insert section.\n");
- goto ERROR;
- }
-
- ELFU_INFO("Inserting %s at address 0x%x...\n",
- elfu_mScnName(mrel, oldscn),
- (unsigned)injAddr);
-
- injOffset = injAddr - injPhdr->phdr.p_vaddr + injPhdr->phdr.p_offset;
-
- newscn->shdr.sh_addr = injAddr;
- newscn->shdr.sh_offset = injOffset;
-
- if (CIRCLEQ_EMPTY(&injPhdr->childScnList)
- || CIRCLEQ_LAST(&injPhdr->childScnList)->shdr.sh_offset < injOffset) {
- CIRCLEQ_INSERT_TAIL(&injPhdr->childScnList, newscn, elemChildScn);
- } else {
- ElfuScn *ms;
- CIRCLEQ_FOREACH(ms, &injPhdr->childScnList, elemChildScn) {
- if (injOffset < ms->shdr.sh_offset) {
- CIRCLEQ_INSERT_BEFORE(&injPhdr->childScnList, ms, newscn, elemChildScn);
- break;
- }
- }
- }
-
-
- /* Inject name */
- if (me->shstrtab) {
- char *newname;
- size_t newnamelen;
-
- newnamelen = strlen("reladd") + 1;
- if (elfu_mScnName(mrel, oldscn)) {
- newnamelen += strlen(elfu_mScnName(mrel, oldscn));
- }
-
- newname = malloc(newnamelen);
- strcpy(newname, "reladd");
- strcat(newname, elfu_mScnName(mrel, oldscn));
-
- if (!newname) {
- ELFU_WARN("insertSection: malloc() failed for newname. Leaving section name empty.\n");
- newscn->shdr.sh_name = 0;
- } else {
- size_t offset = me->shstrtab->shdr.sh_size;
-
- if (!appendData(me->shstrtab, newname, newnamelen)) {
- newscn->shdr.sh_name = offset;
- }
-
- free(newname);
- }
- }
-
- return newscn;
- } else {
- ELFU_WARN("insertSection: Skipping non-memory section %s (type %d flags %u).\n",
- elfu_mScnName(mrel, oldscn),
- oldscn->shdr.sh_type,
- (unsigned)oldscn->shdr.sh_flags);
- goto ERROR;
- }
-
- ERROR:
- if (newscn) {
- // TODO: Destroy newscn
- }
- return NULL;
-}
-
-
-static void* subScnAdd1(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2)
-{
- ElfuScn *newscn;
- ElfuElf *me = (ElfuElf*)aux1;
- (void)aux2;
-
-
- switch(ms->shdr.sh_type) {
- case SHT_PROGBITS: /* 1 */
- case SHT_NOBITS: /* 8 */
- /* Ignore empty sections */
- if (ms->shdr.sh_size == 0) {
- break;
- }
-
- /* Find a place where it belongs and shove it in. */
- newscn = insertSection(me, mrel, ms);
- if (!newscn) {
- ELFU_WARN("mReladd: Could not insert section %s (type %d), skipping.\n",
- elfu_mScnName(mrel, ms),
- ms->shdr.sh_type);
- }
- break;
- }
-
- return NULL;
-}
-
-
-static void* subScnAdd2(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2)
-{
- ElfuElf *me = (ElfuElf*)aux1;
- (void)aux2;
-
- switch(ms->shdr.sh_type) {
- case SHT_NULL: /* 0 */
- case SHT_PROGBITS: /* 1 */
- case SHT_STRTAB: /* 3 */
- case SHT_NOBITS: /* 8 */
- break;
-
-
- case SHT_REL: /* 9 */
- /* Relocate. */
- elfu_mRelocate(me, elfu_mScnByOldscn(me, ms->infoptr), ms);
- break;
-
- case SHT_RELA: /* 4 */
- // TODO: Needs a parser
- //elfu_mRelocate(elfu_mScnByOldscn(me, ms->infoptr), ms);
-
- case SHT_SYMTAB: /* 2 */
- /* Merge with the existing table. Take care of string tables also. */
-
- /* The next section types either do not occur in .o files, or are
- * not strictly necessary to process here. */
- case SHT_NOTE: /* 7 */
- case SHT_HASH: /* 5 */
- case SHT_DYNAMIC: /* 6 */
- case SHT_SHLIB: /* 10 */
- case SHT_DYNSYM: /* 11 */
- case SHT_INIT_ARRAY: /* 14 */
- case SHT_FINI_ARRAY: /* 15 */
- case SHT_PREINIT_ARRAY: /* 16 */
- case SHT_GROUP: /* 17 */
- case SHT_SYMTAB_SHNDX: /* 18 */
- case SHT_NUM: /* 19 */
- default:
- ELFU_WARN("mReladd: Skipping section %s (type %d).\n",
- elfu_mScnName(mrel, ms),
- ms->shdr.sh_type);
- }
-
- return NULL;
-}
-
-
-
-static void insertSymClone(ElfuElf *me, const ElfuScn *oldmsst, const ElfuSym *oldsym)
-{
- GElf_Xword newsize;
- char *newbuf;
- ElfuScn *newscn = NULL;
- ElfuSym *newsym;
- char *oldsymname;
-
- assert(me);
- assert(oldmsst);
- assert(oldsym);
-
- /* If the old symbol pointed to a section, find its clone in the target */
- if (oldsym->scnptr) {
- newscn = elfu_mScnByOldscn(me, oldsym->scnptr);
-
- /* If we didn't copy the section referenced, we won't
- * copy this symbol either */
- if (!newscn) {
- return;
- }
- }
-
- // TODO: Allocate symtab if none present
- assert(me->symtab);
-
- /* Allocate memory for the cloned symbol */
- newsym = malloc(sizeof(*newsym));
- if (!newsym) {
- ELFU_WARN("insertSymClone: malloc() failed for newsym.\n");
- goto ERROR;
- }
-
- oldsymname = ELFU_SYMSTR(oldmsst, oldsym->name);
-
- /* Expand .strtab, append symbol name, link newsym to it */
- newsize = me->symtab->linkptr->shdr.sh_size + strlen(oldsymname) + 1;
- newbuf = realloc(me->symtab->linkptr->data.d_buf, newsize);
- if (!newbuf) {
- ELFU_WARN("insertSymClone: realloc() failed for strtab.\n");
- goto ERROR;
- }
-
- me->symtab->linkptr->data.d_buf = newbuf;
-
- newsym->name = me->symtab->linkptr->shdr.sh_size;
-
- strcpy(newbuf + newsym->name, oldsymname);
-
- me->symtab->linkptr->data.d_size = newsize;
- me->symtab->linkptr->shdr.sh_size = newsize;
-
-
- /* Copy all other fields */
- newsym->scnptr = newscn;
- newsym->shndx = oldsym->shndx; /* If scnptr == NULL, this becomes relevant */
- newsym->bind = oldsym->bind;
- newsym->other = oldsym->other;
- newsym->size = oldsym->size;
- newsym->type = oldsym->type;
- newsym->value = oldsym->value;
-
- /* In executables, symbol addresses need to be in memory */
- if (newscn) {
- newsym->value += newscn->shdr.sh_addr;
- }
-
- /* Insert symbol */
- CIRCLEQ_INSERT_TAIL(&me->symtab->symtab.syms, newsym, elem);
-
- return;
-
- ERROR:
- if (newsym) {
- free(newsym);
- }
-}
-
-static void mergeSymtab(ElfuElf *me, const ElfuElf *mrel)
-{
- ElfuSym *sym;
-
- assert(me);
- assert(mrel);
-
- CIRCLEQ_FOREACH(sym, &mrel->symtab->symtab.syms, elem) {
- insertSymClone(me, mrel->symtab, sym);
- }
-}
-
-
-
-void elfu_mReladd(ElfuElf *me, const ElfuElf *mrel)
-{
- assert(me);
- assert(mrel);
-
- /* For each section in object file, guess how to insert it */
- elfu_mScnForall((ElfuElf*)mrel, subScnAdd1, me, NULL);
-
- mergeSymtab(me, mrel);
-
- /* Do relocations and other stuff */
- elfu_mScnForall((ElfuElf*)mrel, subScnAdd2, me, NULL);
-
- /* Re-layout to accommodate new contents */
- elfu_mLayoutAuto(me);
-}
+++ /dev/null
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <libelfu/libelfu.h>
-
-
-/* Apply relocation information from section *msrt to data in
- * section *mstarget (which is stored in *metarget). */
-void elfu_mRelocate(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt)
-{
- ElfuRel *rel;
-
- assert(mstarget);
- assert(msrt);
-
- ELFU_DEBUG("Relocating in section of type %u size %x\n",
- mstarget->shdr.sh_type,
- (unsigned)mstarget->shdr.sh_size);
-
- CIRCLEQ_FOREACH(rel, &msrt->reltab.rels, elem) {
- Elf32_Word *dest32 = (Elf32_Word*)(((char*)(mstarget->data.d_buf)) + rel->offset);
- Elf64_Word *dest64 = (Elf64_Word*)(((char*)(mstarget->data.d_buf)) + rel->offset);
-
-
- if (metarget->elfclass == ELFCLASS32) {
- Elf32_Word a32 = rel->addendUsed ? rel->addend : *dest32;
- Elf32_Addr p32 = mstarget->shdr.sh_addr + rel->offset;
- Elf32_Addr s32 = elfu_mSymtabLookupVal(metarget, msrt->linkptr, rel->sym);
- switch(rel->type) {
- case R_386_NONE:
- ELFU_DEBUG("Skipping relocation: R_386_NONE\n");
- break;
- case R_386_32:
- *dest32 = s32 + a32;
- break;
- case R_386_PC32:
- *dest32 = s32 + a32 - p32;
- break;
-
- default:
- ELFU_DEBUG("Skipping relocation: Unknown type %d\n", rel->type);
- }
- } else if (metarget->elfclass == ELFCLASS64) {
- Elf64_Word a64 = rel->addend;
- Elf64_Addr p64 = mstarget->shdr.sh_addr + rel->offset;
- Elf64_Addr s64 = elfu_mSymtabLookupVal(metarget, msrt->linkptr, rel->sym);
-
- /* x86-64 only uses RELA with explicit addend. */
- assert(rel->addendUsed);
-
- switch(rel->type) {
- case R_X86_64_NONE:
- ELFU_DEBUG("Skipping relocation: R_386_NONE\n");
- break;
- case R_X86_64_64:
- *dest64 = s64 + a64;
- break;
- case R_X86_64_PC32:
- *dest32 = s64 + a64 - p64;
- break;
- case R_X86_64_32:
- *dest32 = s64 + a64;
- break;
-
- default:
- ELFU_DEBUG("Skipping relocation: Unknown type %d", rel->type);
- }
- }
- }
-}
+++ /dev/null
-#include <assert.h>
-#include <stdlib.h>
-#include <libelfu/libelfu.h>
-
-
-/* Meta-functions */
-
-void* elfu_mScnForall(ElfuElf *me, SectionHandlerFunc f, void *aux1, void *aux2)
-{
- ElfuPhdr *mp;
- ElfuScn *ms;
-
- // TODO: Sort PHDRs by offset before interating
-
- CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
- if (mp->phdr.p_type != PT_LOAD) {
- continue;
- }
-
- CIRCLEQ_FOREACH(ms, &mp->childScnList, elemChildScn) {
- void *rv = f(me, ms, aux1, aux2);
-
- if (rv) {
- return rv;
- }
- }
- }
-
- CIRCLEQ_FOREACH(ms, &me->orphanScnList, elemChildScn) {
- void *rv = f(me, ms, aux1, aux2);
-
- if (rv) {
- return rv;
- }
- }
-
- return NULL;
-}
-
-
-
-
-/* Counting */
-
-static void* subCounter(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
-{
- size_t *i = (size_t*)aux1;
- ElfuScn *otherScn = (ElfuScn*)aux2;
-
- if (ms == otherScn) {
- return ms;
- }
-
- *i += 1;
-
- /* Continue */
- return NULL;
-}
-
-
-size_t elfu_mScnCount(ElfuElf *me)
-{
- /* NULL section *is not* counted */
- size_t i = 0;
-
- assert(me);
-
- elfu_mScnForall(me, subCounter, &i, NULL);
-
- return i;
-}
-
-
-/* Returns the index a section would have in the flattened ELF */
-size_t elfu_mScnIndex(ElfuElf *me, ElfuScn *ms)
-{
- /* NULL section *is* counted */
- size_t i = 1;
-
- assert(me);
- assert(ms);
-
- elfu_mScnForall(me, subCounter, &i, ms);
-
- /* If this assertion is broken then ms is not a section in me. */
- assert(i <= elfu_mScnCount(me));
- return i;
-}
-
-
-static void* subOldscn(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
-{
- ElfuScn *otherScn = (ElfuScn*)aux1;
- (void)aux2;
-
- if (ms->oldptr == otherScn) {
- return ms;
- }
-
- /* Continue */
- return NULL;
-}
-
-/* Returns the section with oldscn == oldscn */
-ElfuScn* elfu_mScnByOldscn(ElfuElf *me, ElfuScn *oldscn)
-{
- assert(me);
- assert(oldscn);
-
- return elfu_mScnForall(me, subOldscn, oldscn, NULL);
-}
-
-
-
-
-/* Convenience */
-
-char* elfu_mScnName(ElfuElf *me, ElfuScn *ms)
-{
- assert(me);
- assert(ms);
-
- if (!me->shstrtab) {
- return NULL;
- }
-
- if (!me->shstrtab->data.d_buf) {
- return NULL;
- }
-
- return &((char*)me->shstrtab->data.d_buf)[ms->shdr.sh_name];
-}
-
-
-static void* subScnsToArray(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
-{
- ElfuScn **arr = (ElfuScn**)aux1;
- size_t *i = (size_t*)aux2;
-
- arr[(*i)] = ms;
- *i += 1;
-
- /* Continue */
- return NULL;
-}
-
-static int cmpScnOffs(const void *ms1, const void *ms2)
-{
- ElfuScn *s1;
- ElfuScn *s2;
-
- assert(ms1);
- assert(ms2);
-
- s1 = *(ElfuScn**)ms1;
- s2 = *(ElfuScn**)ms2;
-
- assert(s1);
- assert(s2);
-
-
- if (s1->shdr.sh_offset < s2->shdr.sh_offset) {
- return -1;
- } else if (s1->shdr.sh_offset == s2->shdr.sh_offset) {
- return 0;
- } else /* if (s1->shdr.sh_offset > s2->shdr.sh_offset) */ {
- return 1;
- }
-}
-
-ElfuScn** elfu_mScnSortedByOffset(ElfuElf *me, size_t *count)
-{
- size_t numSecs;
- ElfuScn **sortedSecs;
- size_t i;
-
- assert(me);
-
- /* Sort sections by offset in file */
- numSecs = elfu_mScnCount(me);
- sortedSecs = malloc(numSecs * sizeof(*sortedSecs));
- if (!sortedSecs) {
- ELFU_WARN("elfu_mScnSortedByOffset: Failed to allocate memory.\n");
- return NULL;
- }
-
- i = 0;
- elfu_mScnForall(me, subScnsToArray, sortedSecs, &i);
- assert(i == numSecs);
-
- qsort(sortedSecs, numSecs, sizeof(*sortedSecs), cmpScnOffs);
-
- *count = numSecs;
-
- return sortedSecs;
-}
+++ /dev/null
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <libelfu/libelfu.h>
-
-
-static void* subFindByName(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
-{
- char *name = (char*)aux1;
- (void)aux2;
-
- if (elfu_mScnName(me, ms)) {
- if (!strcmp(elfu_mScnName(me, ms), name)) {
- return ms;
- }
- }
-
- /* Continue */
- return NULL;
-}
-
-/* Hazard a guess where a function may be found in the PLT */
-static GElf_Word pltLookupVal(ElfuElf *me, char *name)
-{
- ElfuScn *relplt;
- ElfuScn *plt;
- ElfuRel *rel;
- GElf_Word j;
-
- relplt = elfu_mScnForall(me, subFindByName, ".rel.plt", NULL);
- if (!relplt) {
- ELFU_WARN("dynsymLookupVal: Could not find .rel.plt section in destination ELF.\n");
- return 0;
- }
-
- plt = elfu_mScnForall(me, subFindByName, ".plt", NULL);
- if (!plt) {
- ELFU_WARN("dynsymLookupVal: Could not find .plt section in destination ELF.\n");
- return 0;
- }
-
-
- /* Look up name. If the j-th entry in .rel.plt has the name we are
- * looking for, we assume that the (j+1)-th entry in .plt is machine
- * code to jump to the function.
- * Your mileage may vary, but it works on my GNU binaries. */
- assert(relplt->linkptr);
- j = 0;
- CIRCLEQ_FOREACH(rel, &relplt->reltab.rels, elem) {
- GElf_Word i;
- ElfuSym *sym;
- char *symname;
-
- j++;
-
- /* We only consider runtime relocations for functions.
- * Technically, these relocations write the functions' addresses
- * to the GOT, not the PLT, after the dynamic linker has found
- * them. */
- if ((me->elfclass == ELFCLASS32 && rel->type != R_386_JMP_SLOT)
- || (me->elfclass == ELFCLASS64 && rel->type != R_X86_64_JUMP_SLOT)) {
- continue;
- }
-
- /* Get the (rel->sym)-th symbol from the symbol table that
- * .rel.plt points to. */
- sym = CIRCLEQ_FIRST(&relplt->linkptr->symtab.syms);
- for (i = 1; i < rel->sym; i++) {
- sym = CIRCLEQ_NEXT(sym, elem);
- }
-
- symname = ELFU_SYMSTR(relplt->linkptr, sym->name);
- if (!strcmp(symname, name)) {
- /* If this is the symbol we are looking for, then in an x86 binary
- * the jump to the dynamic symbol is probably at offset (j * 16)
- * from the start of the PLT, where j is the PLT entry and 16 is
- * the number of bytes the machine code in a PLT entry take. */
- GElf_Addr addr = plt->shdr.sh_addr + (16 * j);
- ELFU_DEBUG("dynsymLookupVal: Guessing symbol '%s' is in destination memory at %x (PLT entry #%u).\n", name, (unsigned)addr, j);
- return addr;
- }
- }
-
- ELFU_WARN("dynsymLookupVal: Could not find symbol '%s' in destination ELF.\n", name);
-
- return 0;
-}
-
-
-
-/* Look up a value in the symbol table section *msst which is in *me.
- * If it is not found there, see if we can find it in *me's PLT. */
-GElf_Word elfu_mSymtabLookupVal(ElfuElf *me, ElfuScn *msst, GElf_Word entry)
-{
- GElf_Word i;
- ElfuSym *sym;
- char *symname;
-
- assert(me);
- assert(msst);
- assert(entry > 0);
- assert(!CIRCLEQ_EMPTY(&msst->symtab.syms));
-
- sym = CIRCLEQ_FIRST(&msst->symtab.syms);
- for (i = 1; i < entry; i++) {
- sym = CIRCLEQ_NEXT(sym, elem);
- }
- symname = ELFU_SYMSTR(msst, sym->name);
-
- switch (sym->type) {
- case STT_NOTYPE:
- case STT_OBJECT:
- case STT_FUNC:
- if (sym->scnptr) {
- ElfuScn *newscn = elfu_mScnByOldscn(me, sym->scnptr);
- assert(newscn);
- return newscn->shdr.sh_addr + sym->value;
- } else if (sym->shndx == SHN_UNDEF) {
- /* Look the symbol up in .rel.plt. If it cannot be found there then
- * .rel.dyn may need to be expanded with a COPY relocation so the
- * dynamic linker fixes up the (TODO). */
- return pltLookupVal(me, symname);
- } else if (sym->shndx == SHN_ABS) {
- return sym->value;
- } else {
- ELFU_WARN("symtabLookupVal: Symbol binding COMMON is not supported, using 0.\n");
- return 0;
- }
- break;
- case STT_SECTION:
- assert(sym->scnptr);
- assert(elfu_mScnByOldscn(me, sym->scnptr));
- return elfu_mScnByOldscn(me, sym->scnptr)->shdr.sh_addr;
- case STT_FILE:
- ELFU_WARN("symtabLookupVal: Symbol type FILE is not supported, using 0.\n");
- return 0;
- default:
- ELFU_WARN("symtabLookupVal: Unknown symbol type %d for %s.\n", sym->type, symname);
- return 0;
- }
-}
-
-
-/* Look up a value in the symbol table section *msst which is in *me. */
-GElf_Word elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name)
-{
- ElfuSym *sym;
-
- assert(me);
- assert(msst);
- assert(name);
- assert(strlen(name) > 0);
- assert(!CIRCLEQ_EMPTY(&msst->symtab.syms));
-
- CIRCLEQ_FOREACH(sym, &msst->symtab.syms, elem) {
- char *symname = ELFU_SYMSTR(msst, sym->name);
-
- if (!strcmp(symname, name)) {
- goto SYMBOL_FOUND;
- }
- }
- return 0;
-
-
- SYMBOL_FOUND:
-
- switch (sym->type) {
- case STT_NOTYPE:
- case STT_OBJECT:
- case STT_FUNC:
- if (sym->scnptr) {
- GElf_Addr a = sym->value;
- a += me->ehdr.e_type == ET_REL ? sym->scnptr->shdr.sh_addr : 0;
- return a;
- } else if (sym->shndx == SHN_UNDEF) {
- return 0;
- } else if (sym->shndx == SHN_ABS) {
- return sym->value;
- } else {
- ELFU_WARN("elfu_mSymtabLookupAddrByName: Symbol binding COMMON is not supported, using 0.\n");
- return 0;
- }
- break;
- default:
- return 0;
- }
-}
-
-
-
-/* Convert symtab from memory model to elfclass specific format */
-void elfu_mSymtabFlatten(ElfuElf *me)
-{
- ElfuSym *sym;
- size_t numsyms = 0;
-
- elfu_mLayoutAuto(me);
-
- /* Update section indexes and count symbols */
- CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) {
- if (sym->scnptr) {
- sym->shndx = elfu_mScnIndex(me, sym->scnptr);
- }
-
- numsyms++;
- }
-
- /* Copy symbols to elfclass-specific format */
- if (me->elfclass == ELFCLASS32) {
- size_t newsize = (numsyms + 1) * sizeof(Elf32_Sym);
- size_t i;
-
- if (me->symtab->data.d_buf) {
- free(me->symtab->data.d_buf);
- }
- me->symtab->data.d_buf = malloc(newsize);
- assert(me->symtab->data.d_buf);
-
- me->symtab->data.d_size = newsize;
- me->symtab->shdr.sh_size = newsize;
- memset(me->symtab->data.d_buf, 0, newsize);
-
- i = 1;
- CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) {
- Elf32_Sym *es = ((Elf32_Sym*)me->symtab->data.d_buf) + i;
-
- es->st_name = sym->name;
- es->st_value = sym->value;
- es->st_size = sym->size;
- es->st_info = ELF32_ST_INFO(sym->bind, sym->type);
- es->st_other = sym->other;
- es->st_shndx = sym->shndx;
-
- i++;
- }
- } else if (me->elfclass == ELFCLASS64) {
- size_t newsize = (numsyms + 1) * sizeof(Elf64_Sym);
- size_t i;
-
- if (me->symtab->data.d_buf) {
- free(me->symtab->data.d_buf);
- }
- me->symtab->data.d_buf = malloc(newsize);
- assert(me->symtab->data.d_buf);
-
- me->symtab->data.d_size = newsize;
- me->symtab->shdr.sh_size = newsize;
- memset(me->symtab->data.d_buf, 0, newsize);
-
- i = 1;
- CIRCLEQ_FOREACH(sym, &me->symtab->symtab.syms, elem) {
- Elf64_Sym *es = ((Elf64_Sym*)me->symtab->data.d_buf) + i;
-
- es->st_name = sym->name;
- es->st_value = sym->value;
- es->st_size = sym->size;
- es->st_info = ELF64_ST_INFO(sym->bind, sym->type);
- es->st_other = sym->other;
- es->st_shndx = sym->shndx;
-
- i++;
- }
- } else {
- /* Unknown elfclass */
- assert(0);
- }
-
- elfu_mLayoutAuto(me);
-}
+++ /dev/null
-#include <assert.h>
-#include <stdlib.h>
-#include <string.h>
-#include <libelfu/libelfu.h>
-
-
-static void modelToPhdrs(ElfuElf *me, Elf *e)
-{
- ElfuPhdr *mp;
- size_t i;
-
- /* Count PHDRs */
- i = 0;
- CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
- i++;
- }
-
- if (!gelf_newphdr(e, i)) {
- ELFU_WARNELF("gelf_newphdr");
- }
-
- /* Copy PHDRs */
- i = 0;
- CIRCLEQ_FOREACH(mp, &me->phdrList, elem) {
- if (!gelf_update_phdr (e, i, &mp->phdr)) {
- ELFU_WARNELF("gelf_update_phdr");
- }
-
- i++;
- }
-}
-
-
-
-static void* modelToSection(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
-{
- Elf_Scn *scnOut;
- Elf *e = (Elf*)aux1;
- (void) me;
- (void) aux2;
-
- scnOut = elf_newscn(e);
- if (!scnOut) {
- ELFU_WARNELF("elf_newscn");
- return (void*)-1;
- }
-
-
- /* SHDR */
- if (ms->linkptr) {
- ms->shdr.sh_link = elfu_mScnIndex(me, ms->linkptr);
- }
- if (ms->infoptr) {
- ms->shdr.sh_info = elfu_mScnIndex(me, ms->infoptr);
- }
- if (!gelf_update_shdr(scnOut, &ms->shdr)) {
- ELFU_WARNELF("gelf_update_shdr");
- }
-
-
- /* Data */
- if (ms->data.d_buf) {
- Elf_Data *dataOut = elf_newdata(scnOut);
- if (!dataOut) {
- ELFU_WARNELF("elf_newdata");
- }
-
- dataOut->d_align = ms->data.d_align;
- dataOut->d_buf = ms->data.d_buf;
- dataOut->d_off = ms->data.d_off;
- dataOut->d_type = ms->data.d_type;
- dataOut->d_size = ms->data.d_size;
- dataOut->d_version = ms->data.d_version;
- }
-
- return NULL;
-}
-
-
-
-
-
-void elfu_mToElf(ElfuElf *me, Elf *e)
-{
- if (me->symtab) {
- elfu_mSymtabFlatten(me);
- }
-
-
- /* We control the ELF file's layout now. */
- /* tired's libelf also offers ELF_F_LAYOUT_OVERLAP for overlapping sections,
- * but we don't want that since we filtered it out in the reading stage
- * already. It would be too mind-blowing to handle the dependencies between
- * the PHDRs and sections then... */
- elf_flagelf(e, ELF_C_SET, ELF_F_LAYOUT);
-
-
- /* EHDR */
- if (!gelf_newehdr(e, me->elfclass)) {
- ELFU_WARNELF("gelf_newehdr");
- }
-
- if (me->shstrtab) {
- me->ehdr.e_shstrndx = elfu_mScnIndex(me, me->shstrtab);
- }
-
- if (!gelf_update_ehdr(e, &me->ehdr)) {
- ELFU_WARNELF("gelf_update_ehdr");
- }
-
-
- /* Sections */
- elfu_mScnForall(me, modelToSection, e, NULL);
-
-
- /* PHDRs */
- modelToPhdrs(me, e);
-
-
- elf_flagelf(e, ELF_C_SET, ELF_F_DIRTY);
-}