Separate PLT lookup
authornorly <ny-git@enpas.org>
Wed, 26 Jun 2013 18:57:39 +0000 (19:57 +0100)
committernorly <ny-git@enpas.org>
Wed, 26 Jun 2013 20:33:14 +0000 (21:33 +0100)
include/libelfu/modelops.h
src/elfucli.c
src/libelfu/model/symtab.c
src/libelfu/modelops/dynlookup.c [new file with mode: 0644]
src/libelfu/modelops/reladd.c
src/libelfu/modelops/relocate.c

index c0a5851bcd6987c28c8063edc79801f9866c3e51..3ebf0a3344188f0bfa71347648195bac0de91c84 100644 (file)
 #define ELFU_SYMSTR(symtabscn, off) ((symtabscn)->linkptr->databuf + (off))
 
 
-GElf_Word elfu_mSymtabLookupVal(ElfuElf *me, ElfuScn *msst, GElf_Word entry);
-GElf_Word elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name);
-void elfu_mSymtabFlatten(ElfuElf *me);
-
-
-void elfu_mRelocate(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt);
+      int elfu_mSymtabLookupSymToAddr(ElfuElf *me, ElfuScn *msst, ElfuSym *sym, GElf_Addr *result);
+    char* elfu_mSymtabSymToName(ElfuScn *msst, ElfuSym *sym);
+ ElfuSym* elfu_mSymtabIndexToSym(ElfuScn *msst, GElf_Word entry);
+GElf_Addr elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name);
+     void elfu_mSymtabFlatten(ElfuElf *me);
 
 
 typedef void* (PhdrHandlerFunc)(ElfuElf *me, ElfuPhdr *mp, void *aux1, void *aux2);
@@ -54,7 +53,10 @@ GElf_Addr elfu_mLayoutGetSpaceInPhdr(ElfuElf *me, GElf_Word size,
 int elfu_mLayoutAuto(ElfuElf *me);
 
 
-void elfu_mRelocate(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt);
+int elfu_mDynLookupPltAddrByName(ElfuElf *me, char *name, GElf_Addr *result);
+
+
+int elfu_mRelocate(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt);
 
 
 int elfu_mCheck(ElfuElf *me);
@@ -68,7 +70,7 @@ void elfu_mDumpElf(ElfuElf *me);
 ElfuElf* elfu_mFromElf(Elf *e);
     void elfu_mToElf(ElfuElf *me, Elf *e);
 
-void elfu_mReladd(ElfuElf *me, const ElfuElf *mrel);
+ int elfu_mReladd(ElfuElf *me, const ElfuElf *mrel);
 
 void elfu_mDetour(ElfuElf *me, GElf_Addr from, GElf_Addr to);
 
index b3ea29f2440381c68f685c051215cdc3da8cdc27..d5f55be88b11eadc81ee30d9caeee9bdd03cb838 100644 (file)
@@ -149,8 +149,14 @@ int main(int argc, char **argv)
             goto ERR;
           } else {
             printf("--reladd: Injecting %s...\n", optarg);
-            elfu_mCheck(mrel);
-            elfu_mReladd(me, mrel);
+            if (elfu_mCheck(mrel)) {
+              printf("--reladd: Check for input file failed.\n");
+              goto ERR;
+            }
+            if (elfu_mReladd(me, mrel)) {
+              printf("--reladd: Failed.\n");
+              goto ERR;
+            }
             printf("--reladd: Injected %s.\n", optarg);
           }
         }
index 1dd778e43c2519307bc87d32c13ef6267cc2e6e1..0cf107be0fadcffb31ed376e00983ff706236453 100644 (file)
 #include <libelfu/libelfu.h>
 
 
-static void* subFindByName(ElfuElf *me, ElfuScn *ms, void *aux1, void *aux2)
+int elfu_mSymtabLookupSymToAddr(ElfuElf *me, ElfuScn *msst, ElfuSym *sym, GElf_Addr *result)
 {
-  char *name = (char*)aux1;
-  (void)aux2;
+  char *symname = ELFU_SYMSTR(msst, sym->name);
 
-  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) {
-    /* x86-64 uses .rela.plt instead */
-    relplt = elfu_mScnForall(me, subFindByName, ".rela.plt", NULL);
-  }
-  if (!relplt) {
-    ELFU_WARN("dynsymLookupVal: Could not find .rel.plt section in destination ELF.\n");
-    return 0;
-  }
+  switch (sym->type) {
+    case STT_NOTYPE:
+    case STT_OBJECT:
+    case STT_FUNC:
+      if (sym->shndx == SHN_UNDEF) {
+        return -1;
+      } else if (sym->shndx == SHN_ABS) {
+        *result = sym->value;
+        return 0;
+      } else if (sym->shndx == SHN_COMMON) {
+        return -1;
+      } else {
+        ElfuScn *newscn;
 
-  plt = elfu_mScnForall(me, subFindByName, ".plt", NULL);
-  if (!plt) {
-    ELFU_WARN("dynsymLookupVal: Could not find .plt section in destination ELF.\n");
-    return 0;
+        assert (sym->scnptr);
+        newscn = elfu_mScnByOldscn(me, sym->scnptr);
+        assert(newscn);
+        *result = newscn->shdr.sh_addr + sym->value;
+        return 0;
+      }
+      break;
+    case STT_SECTION:
+      assert(sym->scnptr);
+      assert(elfu_mScnByOldscn(me, sym->scnptr));
+      *result = elfu_mScnByOldscn(me, sym->scnptr)->shdr.sh_addr;
+      return 0;
+    case STT_FILE:
+      ELFU_WARN("elfu_mSymtabLookupSymToAddr: Symbol type FILE is not supported.\n");
+      return -1;
+    default:
+      ELFU_WARN("elfu_mSymtabLookupSymToAddr: Unknown symbol type %d for %s.\n", sym->type, symname);
+      return -1;
   }
+}
 
 
-  /* 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);
+char* elfu_mSymtabSymToName(ElfuScn *msst, ElfuSym *sym)
+{
+  assert(msst);
+  assert(sym);
 
-  return 0;
+  return ELFU_SYMSTR(msst, sym->name);
 }
 
 
 
-/* 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)
+ElfuSym* elfu_mSymtabIndexToSym(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));
@@ -109,44 +68,14 @@ GElf_Word elfu_mSymtabLookupVal(ElfuElf *me, ElfuScn *msst, GElf_Word entry)
   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;
-  }
+  return sym;
 }
 
 
+
 /* Look up a value in the symbol table section *msst which is in *me. */
-GElf_Word elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name)
+GElf_Addr elfu_mSymtabLookupAddrByName(ElfuElf *me, ElfuScn *msst, char *name)
 {
   ElfuSym *sym;
 
diff --git a/src/libelfu/modelops/dynlookup.c b/src/libelfu/modelops/dynlookup.c
new file mode 100644 (file)
index 0000000..a2cd3ae
--- /dev/null
@@ -0,0 +1,93 @@
+#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 */
+int elfu_mDynLookupPltAddrByName(ElfuElf *me, char *name, GElf_Addr *result)
+{
+  ElfuScn *relplt;
+  ElfuScn *plt;
+  ElfuRel *rel;
+  GElf_Word j;
+
+  relplt = elfu_mScnForall(me, subFindByName, ".rel.plt", NULL);
+  if (!relplt) {
+    /* x86-64 uses .rela.plt instead */
+    relplt = elfu_mScnForall(me, subFindByName, ".rela.plt", NULL);
+  }
+  if (!relplt) {
+    ELFU_WARN("elfu_mDynLookupPltAddr: Could not find .rel.plt section in destination ELF.\n");
+    return -1;
+  }
+
+  plt = elfu_mScnForall(me, subFindByName, ".plt", NULL);
+  if (!plt) {
+    ELFU_WARN("elfu_mDynLookupPltAddr: Could not find .plt section in destination ELF.\n");
+    return -1;
+  }
+
+
+  /* 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("elfu_mDynLookupPltAddr: Guessing symbol '%s' is in destination memory at %x (PLT entry #%u).\n", name, (unsigned)addr, j);
+      *result = addr;
+      return 0;
+    }
+  }
+
+  ELFU_WARN("elfu_mDynLookupPltAddr: Could not find symbol '%s' in destination ELF.\n", name);
+
+  return -1;
+}
index 55438db523eb237552c7f70f41f6cba31eecb444..bf3b8caab64ed0dc5ba8322281906003e9e365d8 100644 (file)
@@ -210,7 +210,9 @@ static void* subScnAdd2(ElfuElf *mrel, ElfuScn *ms, void *aux1, void *aux2)
     case SHT_RELA: /* 4 */
     case SHT_REL: /* 9 */
       /* Relocate. */
-      elfu_mRelocate(me, elfu_mScnByOldscn(me, ms->infoptr), ms);
+      if (elfu_mRelocate(me, elfu_mScnByOldscn(me, ms->infoptr), ms)) {
+        return (void*)-1;
+      }
       break;
 
     /* The next section types either do not occur in .o files, or are
@@ -328,7 +330,7 @@ static void mergeSymtab(ElfuElf *me, const ElfuElf *mrel)
 
 
 
-void elfu_mReladd(ElfuElf *me, const ElfuElf *mrel)
+int elfu_mReladd(ElfuElf *me, const ElfuElf *mrel)
 {
   assert(me);
   assert(mrel);
@@ -339,8 +341,13 @@ void elfu_mReladd(ElfuElf *me, const ElfuElf *mrel)
   mergeSymtab(me, mrel);
 
   /* Do relocations and other stuff */
-  elfu_mScnForall((ElfuElf*)mrel, subScnAdd2, me, NULL);
+  if (elfu_mScnForall((ElfuElf*)mrel, subScnAdd2, me, NULL)) {
+    ELFU_WARN("elfu_mReladd: Reladd aborted. Target model is unclean.\n");
+    return -1;
+  }
 
   /* Re-layout to accommodate new contents */
   elfu_mLayoutAuto(me);
+
+  return 0;
 }
index 2eada0424e8a27e000df13950770a93619d9f234..7f78524acf962b9af6ce1736376fde770bdbe7dd 100644 (file)
@@ -6,9 +6,10 @@
 
 /* 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)
+int elfu_mRelocate(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt)
 {
   ElfuRel *rel;
+  ElfuSym *sym;
 
   assert(mstarget);
   assert(msrt);
@@ -20,51 +21,100 @@ void elfu_mRelocate(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt)
   CIRCLEQ_FOREACH(rel, &msrt->reltab.rels, elem) {
     Elf32_Word *dest32 = (Elf32_Word*)(mstarget->databuf + rel->offset);
     Elf64_Word *dest64 = (Elf64_Word*)(mstarget->databuf + rel->offset);
+    GElf_Addr s;
+    int haveSymval = 0;
 
+    sym = elfu_mSymtabIndexToSym(msrt->linkptr, rel->sym);
+    assert(sym);
+
+    haveSymval = !elfu_mSymtabLookupSymToAddr(metarget,
+                                              msrt->linkptr,
+                                              sym,
+                                              &s);
+    if (!haveSymval) {
+      if (sym->shndx == SHN_UNDEF) {
+        haveSymval = !elfu_mDynLookupPltAddrByName(metarget,
+                                     elfu_mSymtabSymToName(msrt->linkptr, sym),
+                                     &s);
+      } else if (sym->shndx == SHN_COMMON) {
+        // TODO: Lookup in .rel.dyn
+      }
+    }
 
     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;
+      Elf32_Addr s32 = (Elf32_Addr)s;
 
+      switch(metarget->ehdr.e_machine) {
+        case EM_386:
+          switch(rel->type) {
+            case R_386_NONE:
+              ELFU_DEBUG("Skipping relocation: R_386_NONE\n");
+              break;
+            case R_386_32:
+              if (!haveSymval) {
+                goto MISSINGSYM;
+              }
+              *dest32 = s32 + a32;
+              break;
+            case R_386_PC32:
+              if (!haveSymval) {
+                goto MISSINGSYM;
+              }
+              *dest32 = s32 + a32 - p32;
+              break;
+            default:
+              ELFU_DEBUG("elfu_mRelocate: Skipping unknown relocation type %d\n", rel->type);
+          }
+          break;
         default:
-          ELFU_DEBUG("Skipping relocation: Unknown type %d\n", rel->type);
+          ELFU_WARN("elfu_mRelocate: Unknown machine type. Aborting.\n");
       }
     } else if (metarget->elfclass == ELFCLASS64) {
-      Elf64_Word a64 = rel->addend;
+      Elf64_Word a64 = rel->addendUsed ? rel->addend : *dest64;
       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);
+      Elf64_Addr s64 = (Elf64_Addr)s;
 
-      switch(rel->type) {
-        case R_X86_64_NONE:
-          ELFU_DEBUG("Skipping relocation: R_386_NONE\n");
+      switch(metarget->ehdr.e_machine) {
+        case EM_X86_64:
+          switch(rel->type) {
+            case R_X86_64_NONE:
+              ELFU_DEBUG("Skipping relocation: R_386_NONE\n");
+              break;
+            case R_X86_64_64:
+              if (!haveSymval) {
+                goto MISSINGSYM;
+              }
+              *dest64 = s64 + a64;
+              break;
+            case R_X86_64_PC32:
+              if (!haveSymval) {
+                goto MISSINGSYM;
+              }
+              *dest32 = s64 + a64 - p64;
+              break;
+            case R_X86_64_32:
+              if (!haveSymval) {
+                goto MISSINGSYM;
+              }
+              *dest32 = s64 + a64;
+              break;
+            default:
+              ELFU_DEBUG("elfu_mRelocate: Skipping unknown relocation type %d", rel->type);
+          }
           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);
+          ELFU_WARN("elfu_mRelocate: Unknown machine type. Aborting.\n");
       }
     }
   }
+
+  return 0;
+
+  MISSINGSYM:
+  ELFU_WARN("elfu_mRelocate: Could not resolve symbol %s. Aborting.\n",
+            elfu_mSymtabSymToName(msrt->linkptr, sym));
+  return -1;
+
 }