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