Basic x86-64 support, not very usable in practice
[centaur.git] / src / modelops / relocate.c
index 972bda32bbff6af9809671a2822fe5e4dce1de1f..2c9873c323819b16f41a6f9a683a0dd4876256d0 100644 (file)
@@ -40,31 +40,37 @@ static GElf_Word pltLookupVal(ElfuElf *metarget, char *name)
   }
 
 
-  /* Look up name */
-  assert(relplt->reltab);
+  /* 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);
-  assert(relplt->linkptr->symtab);
   j = 0;
-  CIRCLEQ_FOREACH(rel, &relplt->reltab->rels, elem) {
+  CIRCLEQ_FOREACH(rel, &relplt->reltab.rels, elem) {
     GElf_Word i;
     ElfuSym *sym;
+    char *symname;
 
     j++;
 
-    if (rel->type != R_386_JMP_SLOT) {
+    /* 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 ((metarget->elfclass == ELFCLASS32 && rel->type != R_386_JMP_SLOT)
+        || (metarget->elfclass == ELFCLASS64 && rel->type != R_X86_64_JUMP_SLOT)) {
       continue;
     }
 
-    sym = CIRCLEQ_FIRST(&relplt->linkptr->symtab->syms);
+    /* 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->name) {
-      continue;
-    }
-
-    if (!strcmp(sym->name, 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
@@ -85,30 +91,32 @@ static GElf_Word symtabLookupVal(ElfuElf *metarget, ElfuScn *msst, GElf_Word ent
 {
   GElf_Word i;
   ElfuSym *sym;
+  char *symname;
 
   assert(metarget);
   assert(msst);
-  assert(msst->symtab);
   assert(entry > 0);
-  assert(!CIRCLEQ_EMPTY(&msst->symtab->syms));
+  assert(!CIRCLEQ_EMPTY(&msst->symtab.syms));
 
-  sym = CIRCLEQ_FIRST(&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) {
-        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->name);
+        return pltLookupVal(metarget, symname);
       } else if (sym->shndx == SHN_ABS) {
         return sym->value;
       } else {
@@ -124,7 +132,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->name);
+      ELFU_WARN("symtabLookupVal: Unknown symbol type %d for %s.\n", sym->type, symname);
       return 0;
   }
 }
@@ -140,30 +148,55 @@ void elfu_mRelocate32(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt)
              mstarget->shdr.sh_type,
              mstarget->shdr.sh_size);
 
-  CIRCLEQ_FOREACH(rel, &msrt->reltab->rels, elem) {
-    Elf32_Word *dest = (Elf32_Word*)(((char*)(mstarget->data.d_buf)) + rel->offset);
-    Elf32_Word a = rel->addendUsed ? rel->addend : *dest;
-    Elf32_Addr p = mstarget->shdr.sh_addr + rel->offset;
-    Elf32_Addr s = symtabLookupVal(metarget, msrt->linkptr, rel->sym);
-    Elf32_Word newval = *dest;
-
-    switch(rel->type) {
-      case R_386_NONE:
-        ELFU_DEBUG("Skipping relocation: R_386_NONE");
-        break;
-      case R_386_32:
-        ELFU_DEBUG("Relocation: R_386_32");
-        newval = s + a;
-        break;
-      case R_386_PC32:
-        ELFU_DEBUG("Relocation: R_386_PC32");
-        newval = s + a - p;
-        break;
-
-      default:
-        ELFU_DEBUG("Skipping relocation: Unknown type %d", rel->type);
+  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 = symtabLookupVal(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) {
+      /* x86-64 only uses RELA with explicit addend. */
+      assert(rel->addendUsed);
+      Elf64_Word a64 = rel->addend;
+      Elf64_Addr p64 = mstarget->shdr.sh_addr + rel->offset;
+      Elf64_Addr s64 = symtabLookupVal(metarget, msrt->linkptr, rel->sym);
+
+      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);
+      }
     }
-    ELFU_DEBUG(", overwriting %x with %x.\n", *dest, newval);
-    *dest = newval;
+
+
   }
 }