Basic x86-64 support, not very usable in practice
authornorly <ny-git@enpas.org>
Sat, 15 Jun 2013 20:42:53 +0000 (21:42 +0100)
committernorly <ny-git@enpas.org>
Sat, 15 Jun 2013 21:02:26 +0000 (22:02 +0100)
Turns out that x86-64 aligns LOAD segments to 0x200000 bytes.
Given that the mapping starts at 0x400000, there is not much room to
expand... so there will be a need to add additional LOAD headers.

src/elfops/check.c
src/modelops/fromFile.c
src/modelops/relocate.c
src/modelops/toFile.c

index 6e56bae96b92008b2fe2f9cd383f8733f2abb327..e184b268493f83460f09178d1ecf40247327db17 100644 (file)
@@ -24,8 +24,8 @@ int elfu_eCheck(Elf *e)
     goto ERROR;
   }
 
-  if (ehdr.e_machine != EM_386) {
-    ELFU_WARN("Sorry, only x86-32 ELF files are supported at the moment.\n");
+  if (ehdr.e_machine != EM_386 && ehdr.e_machine != EM_X86_64) {
+    ELFU_WARN("Sorry, only x86-32 and x86-64 ELF files are supported at the moment.\n");
     goto ERROR;
   }
 
index 28ecb85cdb12e984e6b7b2e669b2440fcc8f8f42..bc089bfe99a9b427983091daad31474f03a62ed0 100644 (file)
@@ -5,41 +5,69 @@
 #include <libelfu/libelfu.h>
 
 
-static void parseSymtab32(ElfuScn *ms, ElfuScn**origScnArr)
+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);
 
-  for (i = 1; (i + 1) * sizeof(Elf32_Sym) <= ms->shdr.sh_size; i++) {
-    Elf32_Sym *cursym = &(((Elf32_Sym*)ms->data.d_buf)[i]);
-    ElfuSym *sym = malloc(sizeof(*sym));
-    assert(sym);
+      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;
 
-    sym->name = 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) {
+
+      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[cursym->st_shndx - 1];
+        sym->scnptr = origScnArr[sym->shndx - 1];
         break;
     }
-
-
-    CIRCLEQ_INSERT_TAIL(&ms->symtab.syms, sym, elem);
   }
 }
 
@@ -60,10 +88,8 @@ static void parseReltab32(ElfuScn *ms)
     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;
 
@@ -72,6 +98,32 @@ static void parseReltab32(ElfuScn *ms)
 }
 
 
+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)
 {
   assert(ms1);
@@ -360,11 +412,7 @@ ElfuElf* elfu_mFromElf(Elf *e)
         case SHT_SYMTAB:
           me->symtab = ms;
         case SHT_DYNSYM:
-          if (me->elfclass == ELFCLASS32) {
-            parseSymtab32(ms, secArray);
-          } else if (me->elfclass == ELFCLASS64) {
-            // TODO
-          }
+          parseSymtab(me, ms, secArray);
           break;
       }
     }
@@ -379,14 +427,14 @@ ElfuElf* elfu_mFromElf(Elf *e)
           if (me->elfclass == ELFCLASS32) {
             parseReltab32(ms);
           } else if (me->elfclass == ELFCLASS64) {
-            // TODO
+            // Not used on x86-64
           }
           break;
         case SHT_RELA:
           if (me->elfclass == ELFCLASS32) {
             // TODO
           } else if (me->elfclass == ELFCLASS64) {
-            // TODO
+            parseRelatab64(ms);
           }
           break;
       }
index ea8493754be51868a933c040250e908bce19b818..2c9873c323819b16f41a6f9a683a0dd4876256d0 100644 (file)
@@ -57,7 +57,8 @@ static GElf_Word pltLookupVal(ElfuElf *metarget, char *name)
      * 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) {
+    if ((metarget->elfclass == ELFCLASS32 && rel->type != R_386_JMP_SLOT)
+        || (metarget->elfclass == ELFCLASS64 && rel->type != R_X86_64_JUMP_SLOT)) {
       continue;
     }
 
@@ -148,29 +149,54 @@ void elfu_mRelocate32(ElfuElf *metarget, ElfuScn *mstarget, ElfuScn *msrt)
              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);
+    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;
+
+
   }
 }
index 434130ec0729bc4be1b4a1ffa4289394f3ae2874..90dc2478395da38c93ec1187f6630bd9ab0f51b9 100644 (file)
@@ -49,10 +49,34 @@ static void flattenSymtab(ElfuElf *me)
       i++;
     }
   } else if (me->elfclass == ELFCLASS64) {
-    // TODO
-    assert(0);
+    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 {
-    // Never reached
+    // Unknown elfclass
     assert(0);
   }