Merge symbol tables. (Not fully ELF conformant)
authornorly <ny-git@enpas.org>
Sat, 15 Jun 2013 15:27:28 +0000 (16:27 +0100)
committernorly <ny-git@enpas.org>
Sat, 15 Jun 2013 15:32:15 +0000 (16:32 +0100)
Also, remove that nameptr member from symbols. It just *had* to cause
trouble.

Symbols are simply appended to the target's symbol table, which means
that LOCAL symbols are not inserted at the beginning and we are thus
ignoring an ELF spec. Might change that in the future, it's good enough
for now and it's sure not to break anything with the old symbols.

The code currently assumes that the target *has* a symbol table. We'll
have to fix that, and also remove undefined and duplicate symbols.

include/libelfu/modelops.h
include/libelfu/types.h
src/modelops/fromFile.c
src/modelops/reladd.c
src/modelops/relocate.c
src/modelops/toFile.c

index 3daef5285fa8162c3ce0b9b82e4d3afc0f3e68c7..eee7c13285a51c87b92a0140547764ea449b96a0 100644 (file)
@@ -7,6 +7,9 @@
 #include <libelfu/types.h>
 
 
+#define ELFU_SYMSTR(symtabscn, off) (((char*)(symtabscn)->linkptr->data.d_buf) + (off))
+
+
 size_t elfu_mPhdrCount(ElfuElf *me);
 void elfu_mPhdrUpdateChildOffsets(ElfuPhdr *mp);
 
@@ -42,6 +45,6 @@ void elfu_mDumpElf(ElfuElf *me);
 ElfuElf* elfu_mFromElf(Elf *e);
     void elfu_mToElf(ElfuElf *me, Elf *e);
 
-void elfu_mReladd(ElfuElf *me, ElfuElf *mrel);
+void elfu_mReladd(ElfuElf *me, const ElfuElf *mrel);
 
 #endif
index 37e25fa6c574553e290451c47a33bfc5be11e6c6..e4dd26cc2e99cc1e9c2d0e4884ddd5eca40e87cc 100644 (file)
@@ -9,7 +9,6 @@
 
 typedef struct ElfuSym {
   GElf_Word name;
-  char *nameptr;
 
   GElf_Addr value;
   GElf_Word size;
index c46dd6c38dd727af0aaeec5aa0c569e65c010a65..28ecb85cdb12e984e6b7b2e669b2440fcc8f8f42 100644 (file)
@@ -5,17 +5,6 @@
 #include <libelfu/libelfu.h>
 
 
-static char* symstr(ElfuScn *symtab, size_t off)
-{
-  assert(symtab);
-  assert(symtab->linkptr);
-  assert(symtab->linkptr->data.d_buf);
-  assert(off < symtab->linkptr->data.d_size);
-
-  return &(((char*)symtab->linkptr->data.d_buf)[off]);
-}
-
-
 static void parseSymtab32(ElfuScn *ms, ElfuScn**origScnArr)
 {
   size_t i;
@@ -31,19 +20,18 @@ static void parseSymtab32(ElfuScn *ms, ElfuScn**origScnArr)
     assert(sym);
 
     sym->name = cursym->st_name;
-    sym->nameptr = symstr(ms, cursym->st_name);
     sym->value = cursym->st_value;
     sym->size = cursym->st_size;
     sym->bind = ELF32_ST_BIND(cursym->st_info);
     sym->type = ELF32_ST_TYPE(cursym->st_info);
     sym->other = cursym->st_other;
+    sym->shndx = cursym->st_shndx;
 
     switch (cursym->st_shndx) {
       case SHN_UNDEF:
       case SHN_ABS:
       case SHN_COMMON:
         sym->scnptr = NULL;
-        sym->shndx = cursym->st_shndx;
         break;
       default:
         sym->scnptr = origScnArr[cursym->st_shndx - 1];
index 2fdfb48bf25fc56c4a59a791046fba42118668be..936c0bd34c3cd9da8337e171720a0834ca74c693 100644 (file)
@@ -213,16 +213,111 @@ static void* subScnAdd2(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2)
 }
 
 
-void elfu_mReladd(ElfuElf *me, ElfuElf *mrel)
+
+static void insertSymClone(ElfuElf *me, const ElfuScn *oldmsst, const ElfuSym *oldsym)
+{
+  GElf_Xword newsize;
+  void *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(mrel, subScnAdd1, me, NULL);
+  elfu_mScnForall((ElfuElf*)mrel, subScnAdd1, me, NULL);
+
+  mergeSymtab(me, mrel);
 
   /* Do relocations and other stuff */
-  elfu_mScnForall(mrel, subScnAdd2, me, NULL);
+  elfu_mScnForall((ElfuElf*)mrel, subScnAdd2, me, NULL);
 
   /* Re-layout to accommodate new contents */
   elfu_mLayoutAuto(me);
index 0bd8ee80fe1609fea2a06fd879dfc8401085e77f..ea8493754be51868a933c040250e908bce19b818 100644 (file)
@@ -40,29 +40,36 @@ static GElf_Word pltLookupVal(ElfuElf *metarget, char *name)
   }
 
 
-  /* Look up name */
+  /* 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 (rel->type != R_386_JMP_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);
     }
 
-    if (!sym->nameptr) {
-      continue;
-    }
-
-    if (!strcmp(sym->nameptr, name)) {
+    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
@@ -83,6 +90,7 @@ static GElf_Word symtabLookupVal(ElfuElf *metarget, ElfuScn *msst, GElf_Word ent
 {
   GElf_Word i;
   ElfuSym *sym;
+  char *symname;
 
   assert(metarget);
   assert(msst);
@@ -93,19 +101,21 @@ static GElf_Word symtabLookupVal(ElfuElf *metarget, ElfuScn *msst, GElf_Word ent
   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) {
-        assert(elfu_mScnByOldscn(metarget, sym->scnptr));
-        return elfu_mScnByOldscn(metarget, sym->scnptr)->shdr.sh_addr + sym->value;
+        ElfuScn *newscn = elfu_mScnByOldscn(metarget, sym->scnptr);
+        assert(newscn);
+        return newscn->shdr.sh_addr + sym->value;
       } else if (sym->shndx == SHN_UNDEF) {
-        /* Look the symbol up in .dyn.plt. If it cannot be found there then
+        /* 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(metarget, sym->nameptr);
+        return pltLookupVal(metarget, symname);
       } else if (sym->shndx == SHN_ABS) {
         return sym->value;
       } else {
@@ -121,7 +131,7 @@ static GElf_Word symtabLookupVal(ElfuElf *metarget, ElfuScn *msst, GElf_Word ent
       ELFU_WARN("symtabLookupVal: Symbol type FILE is not supported, using 0.\n");
       return 0;
     default:
-      ELFU_WARN("symtabLookupVal: Unknown symbol type %d for %s.\n", sym->type, sym->nameptr);
+      ELFU_WARN("symtabLookupVal: Unknown symbol type %d for %s.\n", sym->type, symname);
       return 0;
   }
 }
index 66109dfb8a2dfefeaad3e5084ee91d3cc7782cce..434130ec0729bc4be1b4a1ffa4289394f3ae2874 100644 (file)
@@ -11,6 +11,7 @@ static void flattenSymtab(ElfuElf *me)
 
   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);
@@ -19,6 +20,7 @@ static void flattenSymtab(ElfuElf *me)
     numsyms++;
   }
 
+  /* Copy symbols to elfclass-specific format */
   if (me->elfclass == ELFCLASS32) {
     size_t newsize = (numsyms + 1) * sizeof(Elf32_Sym);
     size_t i;