Separate PLT lookup
[centaur.git] / src / libelfu / modelops / relocate.c
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;
+
 }