x86_64 linker.

Based on I8dc3e2cb596f75dc58ae82e4dc58f8c177dd3323 by
Pavel Chupin <pavel.v.chupin@intel.com>.

Change-Id: Icd582d277cbe273477b450f2848343d72c86ec9f
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 983d0a0..d218590 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -404,7 +404,7 @@
     soinfo *si;
     unsigned addr = (unsigned)pc;
 
-    for (si = solist; si != 0; si = si->next){
+    for (si = solist; si != 0; si = si->next) {
         if ((addr >= si->base) && (addr < (si->base + si->size))) {
             *pcount = si->ARM_exidx_count;
             return (_Unwind_Ptr)si->ARM_exidx;
@@ -441,23 +441,24 @@
     Elf_Sym* symtab = si->symtab;
     const char* strtab = si->strtab;
 
-    TRACE_TYPE(LOOKUP, "SEARCH %s in %s@0x%08x %08x %zd",
-               name, si->name, si->base, hash, hash % si->nbucket);
+    TRACE_TYPE(LOOKUP, "SEARCH %s in %s@%p %x %zd",
+               name, si->name, reinterpret_cast<void*>(si->base), hash, hash % si->nbucket);
 
     for (unsigned n = si->bucket[hash % si->nbucket]; n != 0; n = si->chain[n]) {
         Elf_Sym* s = symtab + n;
         if (strcmp(strtab + s->st_name, name)) continue;
 
             /* only concern ourselves with global and weak symbol definitions */
-        switch(ELF32_ST_BIND(s->st_info)){
+        switch (ELF_ST_BIND(s->st_info)) {
         case STB_GLOBAL:
         case STB_WEAK:
             if (s->st_shndx == SHN_UNDEF) {
                 continue;
             }
 
-            TRACE_TYPE(LOOKUP, "FOUND %s in %s (%08x) %d",
-                       name, si->name, s->st_value, s->st_size);
+            TRACE_TYPE(LOOKUP, "FOUND %s in %s (%p) %zd",
+                       name, si->name, reinterpret_cast<void*>(s->st_value),
+                       static_cast<size_t>(s->st_size));
             return s;
         }
     }
@@ -568,10 +569,11 @@
 
 done:
     if (s != NULL) {
-        TRACE_TYPE(LOOKUP, "si %s sym %s s->st_value = 0x%08x, "
-                   "found in %s, base = 0x%08x, load bias = 0x%08x",
-                   si->name, name, s->st_value,
-                   (*lsi)->name, (*lsi)->base, (*lsi)->load_bias);
+        TRACE_TYPE(LOOKUP, "si %s sym %s s->st_value = %p, "
+                   "found in %s, base = %p, load bias = %p",
+                   si->name, name, reinterpret_cast<void*>(s->st_value),
+                   (*lsi)->name, reinterpret_cast<void*>((*lsi)->base),
+                   reinterpret_cast<void*>((*lsi)->load_bias));
         return s;
     }
 
@@ -613,8 +615,8 @@
   }
 
   if (s != NULL) {
-    TRACE_TYPE(LOOKUP, "%s s->st_value = 0x%08x, found->base = 0x%08x",
-               name, s->st_value, (*found)->base);
+    TRACE_TYPE(LOOKUP, "%s s->st_value = %p, found->base = %p",
+               name, reinterpret_cast<void*>(s->st_value), reinterpret_cast<void*>((*found)->base));
   }
 
   return s;
@@ -767,8 +769,8 @@
 
   // At this point we know that whatever is loaded @ base is a valid ELF
   // shared library whose segments are properly mapped in.
-  TRACE("[ init_library base=0x%08x sz=0x%08x name='%s' ]",
-        si->base, si->size, si->name);
+  TRACE("[ init_library base=%p sz=0x%08x name='%s' ]",
+        reinterpret_cast<void*>(si->base), si->size, si->name);
 
   if (!soinfo_link_image(si)) {
     munmap(reinterpret_cast<void*>(si->base), si->size);
@@ -838,10 +840,124 @@
   return result;
 }
 
-/* TODO: don't use unsigned for addrs below. It works, but is not
- * ideal. They should probably be either uint32_t, Elf_Addr, or unsigned
- * long.
- */
+#if defined(ANDROID_X86_64_LINKER)
+static int soinfo_relocate_a(soinfo* si, Elf_Rela* rela, unsigned count, soinfo* needed[]) {
+  Elf_Sym* symtab = si->symtab;
+  const char* strtab = si->strtab;
+  Elf_Sym* s;
+  Elf_Rela* start = rela;
+  soinfo* lsi;
+
+  for (size_t idx = 0; idx < count; ++idx, ++rela) {
+    unsigned type = ELF_R_TYPE(rela->r_info);
+    unsigned sym = ELF_R_SYM(rela->r_info);
+    Elf_Addr reloc = static_cast<Elf_Addr>(rela->r_offset + si->load_bias);
+    Elf_Addr sym_addr = 0;
+    char* sym_name = NULL;
+
+    DEBUG("Processing '%s' relocation at index %zd", si->name, idx);
+    if (type == 0) { // R_*_NONE
+      continue;
+    }
+    if (sym != 0) {
+      sym_name = (char *)(strtab + symtab[sym].st_name);
+      s = soinfo_do_lookup(si, sym_name, &lsi, needed);
+      if (s == NULL) {
+        // We only allow an undefined symbol if this is a weak reference...
+        s = &symtab[sym];
+        if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
+          DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, si->name);
+          return -1;
+        }
+
+        /* IHI0044C AAELF 4.5.1.1:
+
+           Libraries are not searched to resolve weak references.
+           It is not an error for a weak reference to remain unsatisfied.
+
+           During linking, the value of an undefined weak reference is:
+           - Zero if the relocation type is absolute
+           - The address of the place if the relocation is pc-relative
+           - The address of nominal base address if the relocation
+             type is base-relative.
+         */
+
+        switch (type) {
+        case R_X86_64_JUMP_SLOT:
+        case R_X86_64_GLOB_DAT:
+        case R_X86_64_32:
+        case R_X86_64_RELATIVE:
+          // No need to do anything.
+          break;
+
+        case R_X86_64_PC32:
+          sym_addr = reloc;
+          break;
+
+        default:
+          DL_ERR("unknown weak reloc type %d @ %p (%d)", type, rela, (int) (rela - start));
+          return -1;
+        }
+      } else {
+        // We got a definition.
+        sym_addr = static_cast<Elf_Addr>(s->st_value + lsi->load_bias);
+      }
+      count_relocation(kRelocSymbol);
+    } else {
+      s = NULL;
+    }
+
+    switch (type) {
+    case R_X86_64_JUMP_SLOT:
+      count_relocation(kRelocAbsolute);
+      MARK(rela->r_offset);
+      TRACE_TYPE(RELO, "RELO JMP_SLOT %08zx <- %08zx %s", static_cast<size_t>(reloc),
+                 static_cast<size_t>(sym_addr + rela->r_addend), sym_name);
+      *reinterpret_cast<Elf_Addr*>(reloc) = sym_addr + rela->r_addend;
+      break;
+    case R_X86_64_GLOB_DAT:
+      count_relocation(kRelocAbsolute);
+      MARK(rela->r_offset);
+      TRACE_TYPE(RELO, "RELO GLOB_DAT %08zx <- %08zx %s", static_cast<size_t>(reloc),
+                 static_cast<size_t>(sym_addr + rela->r_addend), sym_name);
+      *reinterpret_cast<Elf_Addr*>(reloc) = sym_addr + rela->r_addend;
+      break;
+    case R_X86_64_RELATIVE:
+      count_relocation(kRelocRelative);
+      MARK(rela->r_offset);
+      if (sym) {
+        DL_ERR("odd RELATIVE form...");
+        return -1;
+      }
+      TRACE_TYPE(RELO, "RELO RELATIVE %08zx <- +%08zx", static_cast<size_t>(reloc),
+                 static_cast<size_t>(si->base));
+      *reinterpret_cast<Elf_Addr*>(reloc) = si->base + rela->r_addend;
+      break;
+
+    case R_X86_64_32:
+      count_relocation(kRelocRelative);
+      MARK(rela->r_offset);
+      TRACE_TYPE(RELO, "RELO R_X86_64_32 %08zx <- +%08zx %s", static_cast<size_t>(reloc),
+                 static_cast<size_t>(sym_addr), sym_name);
+      *reinterpret_cast<Elf_Addr*>(reloc) = sym_addr + rela->r_addend;
+      break;
+
+    case R_X86_64_PC32:
+      count_relocation(kRelocRelative);
+      MARK(rela->r_offset);
+      TRACE_TYPE(RELO, "RELO R_X86_64_PC32 %08zx <- +%08zx (%08zx - %08zx) %s",
+                 static_cast<size_t>(reloc), static_cast<size_t>(sym_addr - reloc),
+                 static_cast<size_t>(sym_addr), static_cast<size_t>(reloc), sym_name);
+      *reinterpret_cast<Elf_Addr*>(reloc) = sym_addr + rela->r_addend - reloc;
+      break;
+    default:
+      DL_ERR("unknown reloc type %d @ %p (%d)", type, rela, (int) (rela - start));
+      return -1;
+    }
+  }
+  return 0;
+}
+#else
 static int soinfo_relocate(soinfo* si, Elf_Rel* rel, unsigned count,
                            soinfo* needed[])
 {
@@ -852,8 +968,9 @@
     soinfo* lsi;
 
     for (size_t idx = 0; idx < count; ++idx, ++rel) {
-        unsigned type = ELF32_R_TYPE(rel->r_info);
-        unsigned sym = ELF32_R_SYM(rel->r_info);
+        unsigned type = ELF_R_TYPE(rel->r_info);
+        // TODO: don't use unsigned for 'sym'. Use uint32_t or Elf_Addr instead.
+        unsigned sym = ELF_R_SYM(rel->r_info);
         Elf_Addr reloc = static_cast<Elf_Addr>(rel->r_offset + si->load_bias);
         Elf_Addr sym_addr = 0;
         char* sym_name = NULL;
@@ -869,7 +986,7 @@
                 /* We only allow an undefined symbol if this is a weak
                    reference..   */
                 s = &symtab[sym];
-                if (ELF32_ST_BIND(s->st_info) != STB_WEAK) {
+                if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
                     DL_ERR("cannot locate symbol \"%s\" referenced by \"%s\"...", sym_name, si->name);
                     return -1;
                 }
@@ -897,7 +1014,7 @@
                 case R_386_JMP_SLOT:
                 case R_386_GLOB_DAT:
                 case R_386_32:
-                case R_386_RELATIVE:    /* Dont' care. */
+                case R_386_RELATIVE:    /* Don't care. */
 #endif /* ANDROID_*_LINKER */
                     /* sym_addr was initialized to be zero above or relocation
                        code below does not care about value of sym_addr.
@@ -940,7 +1057,7 @@
 /* TODO: This is ugly. Split up the relocations by arch into
  * different files.
  */
-        switch(type){
+        switch (type) {
 #if defined(ANDROID_ARM_LINKER)
         case R_ARM_JUMP_SLOT:
             count_relocation(kRelocAbsolute);
@@ -1005,7 +1122,8 @@
                 DL_ERR("odd RELATIVE form...");
                 return -1;
             }
-            TRACE_TYPE(RELO, "RELO RELATIVE %08x <- +%08x", reloc, si->base);
+            TRACE_TYPE(RELO, "RELO RELATIVE %p <- +%p",
+                       reinterpret_cast<void*>(reloc), reinterpret_cast<void*>(si->base));
             *reinterpret_cast<Elf_Addr*>(reloc) += si->base;
             break;
 
@@ -1080,6 +1198,7 @@
     }
     return 0;
 }
+#endif
 
 #ifdef ANDROID_MIPS_LINKER
 static bool mips_relocate_got(soinfo* si, soinfo* needed[]) {
@@ -1129,7 +1248,7 @@
             /* We only allow an undefined symbol if this is a weak
                reference..   */
             s = &symtab[g];
-            if (ELF32_ST_BIND(s->st_info) != STB_WEAK) {
+            if (ELF_ST_BIND(s->st_info) != STB_WEAK) {
                 DL_ERR("cannot locate \"%s\"...", sym_name);
                 return false;
             }
@@ -1307,7 +1426,7 @@
     /* We can't debug anything until the linker is relocated */
     if (!relocating_linker) {
         INFO("[ linking %s ]", si->name);
-        DEBUG("si->base = 0x%08x si->flags = 0x%08x", si->base, si->flags);
+        DEBUG("si->base = %p si->flags = 0x%08x", reinterpret_cast<void*>(si->base), si->flags);
     }
 
     /* Extract dynamic section */
@@ -1334,8 +1453,9 @@
     // Extract useful information from dynamic section.
     uint32_t needed_count = 0;
     for (Elf_Dyn* d = si->dynamic; d->d_tag != DT_NULL; ++d) {
-        DEBUG("d = %p, d[0](tag) = 0x%08x d[1](val) = 0x%08x", d, d->d_tag, d->d_un.d_val);
-        switch(d->d_tag){
+        DEBUG("d = %p, d[0](tag) = %p d[1](val) = %p",
+              d, reinterpret_cast<void*>(d->d_tag), reinterpret_cast<void*>(d->d_un.d_val));
+        switch (d->d_tag) {
         case DT_HASH:
             si->nbucket = ((unsigned *) (base + d->d_un.d_ptr))[0];
             si->nchain = ((unsigned *) (base + d->d_un.d_ptr))[1];
@@ -1348,28 +1468,34 @@
         case DT_SYMTAB:
             si->symtab = (Elf_Sym *) (base + d->d_un.d_ptr);
             break;
+#if !defined(ANDROID_X86_64_LINKER)
         case DT_PLTREL:
             if (d->d_un.d_val != DT_REL) {
                 DL_ERR("unsupported DT_RELA in \"%s\"", si->name);
                 return false;
             }
             break;
+#endif
         case DT_JMPREL:
+#if defined(ANDROID_X86_64_LINKER)
+            si->plt_rela = (Elf_Rela*) (base + d->d_un.d_ptr);
+#else
             si->plt_rel = (Elf_Rel*) (base + d->d_un.d_ptr);
+#endif
             break;
         case DT_PLTRELSZ:
+#if defined(ANDROID_X86_64_LINKER)
+            si->plt_rela_count = d->d_un.d_val / sizeof(Elf_Rela);
+#else
             si->plt_rel_count = d->d_un.d_val / sizeof(Elf_Rel);
-            break;
-        case DT_REL:
-            si->rel = (Elf_Rel*) (base + d->d_un.d_ptr);
-            break;
-        case DT_RELSZ:
-            si->rel_count = d->d_un.d_val / sizeof(Elf_Rel);
+#endif
             break;
         case DT_PLTGOT:
+#if !defined(ANDROID_X86_64_LINKER)
             /* Save this in case we decide to do lazy binding. We don't yet. */
             si->plt_got = (unsigned *)(base + d->d_un.d_ptr);
             break;
+#endif
         case DT_DEBUG:
             // Set the DT_DEBUG entry to the address of _r_debug for GDB
             // if the dynamic table is writable
@@ -1377,9 +1503,30 @@
                 d->d_un.d_val = reinterpret_cast<uintptr_t>(&_r_debug);
             }
             break;
+#if defined(ANDROID_X86_64_LINKER)
+         case DT_RELA:
+            si->rela = (Elf_Rela*) (base + d->d_un.d_ptr);
+            break;
+         case DT_RELASZ:
+            si->rela_count = d->d_un.d_val / sizeof(Elf_Rela);
+            break;
+        case DT_REL:
+            DL_ERR("unsupported DT_REL in \"%s\"", si->name);
+            return false;
+        case DT_RELSZ:
+            DL_ERR("unsupported DT_RELSZ in \"%s\"", si->name);
+            return false;
+#else
+        case DT_REL:
+            si->rel = (Elf_Rel*) (base + d->d_un.d_ptr);
+            break;
+        case DT_RELSZ:
+            si->rel_count = d->d_un.d_val / sizeof(Elf_Rel);
+            break;
          case DT_RELA:
             DL_ERR("unsupported DT_RELA in \"%s\"", si->name);
             return false;
+#endif
         case DT_INIT:
             si->init_func = reinterpret_cast<linker_function_t>(base + d->d_un.d_ptr);
             DEBUG("%s constructors (DT_INIT) found at %p", si->name, si->init_func);
@@ -1466,8 +1613,8 @@
         }
     }
 
-    DEBUG("si->base = 0x%08x, si->strtab = %p, si->symtab = %p",
-          si->base, si->strtab, si->symtab);
+    DEBUG("si->base = %p, si->strtab = %p, si->symtab = %p",
+          reinterpret_cast<void*>(si->base), si->strtab, si->symtab);
 
     // Sanity checks.
     if (relocating_linker && needed_count != 0) {
@@ -1537,6 +1684,20 @@
         }
     }
 
+#if defined(ANDROID_X86_64_LINKER)
+    if (si->plt_rela != NULL) {
+        DEBUG("[ relocating %s plt ]\n", si->name );
+        if (soinfo_relocate_a(si, si->plt_rela, si->plt_rela_count, needed)) {
+            return false;
+        }
+    }
+    if (si->rela != NULL) {
+        DEBUG("[ relocating %s ]\n", si->name );
+        if (soinfo_relocate_a(si, si->rela, si->rela_count, needed)) {
+            return false;
+        }
+    }
+#else
     if (si->plt_rel != NULL) {
         DEBUG("[ relocating %s plt ]", si->name );
         if (soinfo_relocate(si, si->plt_rel, si->plt_rel_count, needed)) {
@@ -1549,6 +1710,7 @@
             return false;
         }
     }
+#endif
 
 #ifdef ANDROID_MIPS_LINKER
     if (!mips_relocate_got(si, needed)) {
@@ -1781,7 +1943,7 @@
     fflush(stdout);
 #endif
 
-    TRACE("[ Ready to execute '%s' @ 0x%08x ]", si->name, si->entry);
+    TRACE("[ Ready to execute '%s' @ %p ]", si->name, reinterpret_cast<void*>(si->entry));
     return si->entry;
 }