x86_64 linker.

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

Change-Id: Icd582d277cbe273477b450f2848343d72c86ec9f
diff --git a/linker/Android.mk b/linker/Android.mk
index d1773a8..2def99d 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -21,7 +21,8 @@
 LOCAL_CFLAGS += -fno-stack-protector \
         -Wstrict-overflow=5 \
         -fvisibility=hidden \
-        -Wall -Wextra -Werror
+        -Wall -Wextra -Werror \
+        -g
 
 # We need to access Bionic private headers in the linker.
 LOCAL_CFLAGS += -I$(LOCAL_PATH)/../libc/
@@ -30,12 +31,16 @@
     LOCAL_CFLAGS += -DANDROID_ARM_LINKER
 endif
 
+ifeq ($(TARGET_ARCH),mips)
+    LOCAL_CFLAGS += -DANDROID_MIPS_LINKER
+endif
+
 ifeq ($(TARGET_ARCH),x86)
     LOCAL_CFLAGS += -DANDROID_X86_LINKER
 endif
 
-ifeq ($(TARGET_ARCH),mips)
-    LOCAL_CFLAGS += -DANDROID_MIPS_LINKER
+ifeq ($(TARGET_ARCH),x86_64)
+    LOCAL_CFLAGS += -DANDROID_X86_64_LINKER
 endif
 
 LOCAL_MODULE:= linker
diff --git a/linker/arch/x86_64/begin.S b/linker/arch/x86_64/begin.S
new file mode 100644
index 0000000..9ecad1a
--- /dev/null
+++ b/linker/arch/x86_64/begin.S
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *  * Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *  * Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <machine/asm.h>
+
+ENTRY(_start)
+  /* Pass elfdata to __linker_init. */
+  mov %rsp, %rdi
+  call __linker_init
+
+  /* linker init returns (%rax) the _entry address in the main image */
+  jmp *%rax
+END(_start)
diff --git a/linker/debugger.cpp b/linker/debugger.cpp
index c947522..6ddd358 100644
--- a/linker/debugger.cpp
+++ b/linker/debugger.cpp
@@ -176,7 +176,12 @@
  * Catches fatal signals so we can ask debuggerd to ptrace us before
  * we crash.
  */
+#if __LP64__ // TODO: implement 64-bit sigaction using rt_sigaction.
+void debuggerd_signal_handler(int n) {
+    siginfo_t* info = NULL;
+#else
 void debuggerd_signal_handler(int n, siginfo_t* info, void*) {
+#endif
     /*
      * It's possible somebody cleared the SA_SIGINFO flag, which would mean
      * our "info" arg holds an undefined value.
@@ -249,7 +254,11 @@
     struct sigaction action;
     memset(&action, 0, sizeof(action));
     sigemptyset(&action.sa_mask);
+#if __LP64__ // TODO: implement 64-bit sigaction using rt_sigaction.
+    action.sa_handler = debuggerd_signal_handler;
+#else
     action.sa_sigaction = debuggerd_signal_handler;
+#endif
     action.sa_flags = SA_RESTART | SA_SIGINFO;
 
     // Use the alternate signal stack if available so we can catch stack overflows.
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index b438f00..621bcb7 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -100,11 +100,10 @@
   }
 
   if (sym != NULL) {
-    unsigned bind = ELF32_ST_BIND(sym->st_info);
+    unsigned bind = ELF_ST_BIND(sym->st_info);
 
     if (bind == STB_GLOBAL && sym->st_shndx != 0) {
-      unsigned ret = sym->st_value + found->load_bias;
-      return (void*) ret;
+      return reinterpret_cast<void*>(sym->st_value + found->load_bias);
     }
 
     __bionic_format_dlerror("symbol found but not global", symbol);
@@ -150,13 +149,13 @@
 //   0123456 78901234 567890 12345678 9012345 6789012345678901234567890123456 7890123456789012 3456789
 #define ANDROID_LIBDL_STRTAB \
     "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0android_update_LD_LIBRARY_PATH\0dl_iterate_phdr\0dl_unwind_find_exidx\0"
-#elif defined(ANDROID_X86_LINKER) || defined(ANDROID_MIPS_LINKER)
+#elif defined(ANDROID_MIPS_LINKER) || defined(ANDROID_X86_LINKER) || defined(ANDROID_X86_64_LINKER)
 //   0000000 00011111 111112 22222222 2333333 3333444444444455555555556666666 6667
 //   0123456 78901234 567890 12345678 9012345 6789012345678901234567890123456 7890
 #define ANDROID_LIBDL_STRTAB \
     "dlopen\0dlclose\0dlsym\0dlerror\0dladdr\0android_update_LD_LIBRARY_PATH\0dl_iterate_phdr\0"
 #else
-#error Unsupported architecture. Only ARM, MIPS, and x86 are presently supported.
+#error Unsupported architecture. Only ARM, MIPS, x86, and x86_64 are presently supported.
 #endif
 
 // name_offset: starting index of the name in libdl_info.strtab
@@ -166,23 +165,39 @@
       /* st_size */ 0, \
       (shndx == 0) ? 0 : (STB_GLOBAL << 4), \
       /* st_other */ 0, \
-      shndx }
+      shndx, \
+    }
 
-static Elf32_Sym gLibDlSymtab[] = {
+#define ELF64_SYM_INITIALIZER(name_offset, value, shndx) \
+    { name_offset, \
+      (shndx == 0) ? 0 : (STB_GLOBAL << 4), \
+      /* st_other */ 0, \
+      shndx, \
+      reinterpret_cast<Elf64_Addr>(reinterpret_cast<void*>(value)), \
+      /* st_size */ 0, \
+    }
+
+#if defined(__LP64__)
+#  define ELF_SYM_INITIALIZER ELF64_SYM_INITIALIZER
+#else
+#  define ELF_SYM_INITIALIZER ELF32_SYM_INITIALIZER
+#endif
+
+static Elf_Sym gLibDlSymtab[] = {
   // Total length of libdl_info.strtab, including trailing 0.
   // This is actually the STH_UNDEF entry. Technically, it's
   // supposed to have st_name == 0, but instead, it points to an index
   // in the strtab with a \0 to make iterating through the symtab easier.
-  ELF32_SYM_INITIALIZER(sizeof(ANDROID_LIBDL_STRTAB) - 1, NULL, 0),
-  ELF32_SYM_INITIALIZER( 0, &dlopen, 1),
-  ELF32_SYM_INITIALIZER( 7, &dlclose, 1),
-  ELF32_SYM_INITIALIZER(15, &dlsym, 1),
-  ELF32_SYM_INITIALIZER(21, &dlerror, 1),
-  ELF32_SYM_INITIALIZER(29, &dladdr, 1),
-  ELF32_SYM_INITIALIZER(36, &android_update_LD_LIBRARY_PATH, 1),
-  ELF32_SYM_INITIALIZER(67, &dl_iterate_phdr, 1),
+  ELF_SYM_INITIALIZER(sizeof(ANDROID_LIBDL_STRTAB) - 1, NULL, 0),
+  ELF_SYM_INITIALIZER( 0, &dlopen, 1),
+  ELF_SYM_INITIALIZER( 7, &dlclose, 1),
+  ELF_SYM_INITIALIZER(15, &dlsym, 1),
+  ELF_SYM_INITIALIZER(21, &dlerror, 1),
+  ELF_SYM_INITIALIZER(29, &dladdr, 1),
+  ELF_SYM_INITIALIZER(36, &android_update_LD_LIBRARY_PATH, 1),
+  ELF_SYM_INITIALIZER(67, &dl_iterate_phdr, 1),
 #if defined(ANDROID_ARM_LINKER)
-  ELF32_SYM_INITIALIZER(83, &dl_unwind_find_exidx, 1),
+  ELF_SYM_INITIALIZER(83, &dl_unwind_find_exidx, 1),
 #endif
 };
 
@@ -215,9 +230,18 @@
 soinfo libdl_info = {
     "libdl.so",
 
-    phdr: 0, phnum: 0,
-    entry: 0, base: 0, size: 0,
-    unused1: 0, dynamic: 0, unused2: 0, unused3: 0,
+    phdr: 0,
+    phnum: 0,
+    entry: 0,
+    base: 0,
+    size: 0,
+#if !defined(__LP64__)
+    unused1: 0,
+#endif
+    dynamic: 0,
+#if !defined(__LP64__)
+    unused2: 0, unused3: 0,
+#endif
     next: 0,
 
     flags: FLAG_LINKED,
@@ -230,14 +254,38 @@
     bucket: gLibDlBuckets,
     chain: gLibDlChains,
 
-    plt_got: 0, plt_rel: 0, plt_rel_count: 0, rel: 0, rel_count: 0,
-    preinit_array: 0, preinit_array_count: 0, init_array: 0, init_array_count: 0,
-    fini_array: 0, fini_array_count: 0, init_func: 0, fini_func: 0,
+#if defined(ANDROID_X86_64_LINKER)
+    plt_rela: 0,
+    plt_rela_count: 0,
+    rela: 0,
+    rela_count: 0,
+#else
+    plt_got: 0,
+    plt_rel: 0,
+    plt_rel_count: 0,
+    rel: 0,
+    rel_count: 0,
+#endif
+
+    preinit_array: 0,
+    preinit_array_count: 0,
+
+    init_array: 0,
+    init_array_count: 0,
+
+    fini_array: 0,
+    fini_array_count: 0,
+
+    init_func: 0,
+    fini_func: 0,
 
 #if defined(ANDROID_ARM_LINKER)
-    ARM_exidx: 0, ARM_exidx_count: 0,
+    ARM_exidx: 0,
+    ARM_exidx_count: 0,
 #elif defined(ANDROID_MIPS_LINKER)
-    mips_symtabno: 0, mips_local_gotno: 0, mips_gotsym: 0,
+    mips_symtabno: 0,
+    mips_local_gotno: 0,
+    mips_gotsym: 0,
 #endif
 
     ref_count: 0,
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;
 }
 
diff --git a/linker/linker.h b/linker/linker.h
index ac7b9fe..c5202c9 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -128,6 +128,13 @@
   unsigned* bucket;
   unsigned* chain;
 
+#if defined(ANDROID_X86_64_LINKER)
+  Elf_Rela *plt_rela;
+  size_t plt_rela_count;
+
+  Elf_Rela *rela;
+  size_t rela_count;
+#else
   unsigned* plt_got;
 
   Elf_Rel* plt_rel;
@@ -135,6 +142,7 @@
 
   Elf_Rel* rel;
   size_t rel_count;
+#endif
 
   linker_function_t* preinit_array;
   size_t preinit_array_count;
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index bf2cc19..5f2b2c1 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -163,10 +163,29 @@
     return false;
   }
 
-  if (header_.e_ident[EI_CLASS] != ELFCLASS32) {
-    DL_ERR("\"%s\" not 32-bit: %d", name_, header_.e_ident[EI_CLASS]);
+  // Try to give a clear diagnostic for ELF class mismatches, since they're
+  // an easy mistake to make during the 32-bit/64-bit transition period.
+  int elf_class = header_.e_ident[EI_CLASS];
+#if defined(__LP64__)
+  if (elf_class != ELFCLASS64) {
+    if (elf_class == ELFCLASS32) {
+      DL_ERR("\"%s\" is 32-bit instead of 64-bit", name_);
+    } else {
+      DL_ERR("\"%s\" has unknown ELF class: %d", name_, elf_class);
+    }
     return false;
   }
+#else
+  if (elf_class != ELFCLASS32) {
+    if (elf_class == ELFCLASS64) {
+      DL_ERR("\"%s\" is 64-bit instead of 32-bit", name_);
+    } else {
+      DL_ERR("\"%s\" has unknown ELF class: %d", name_, elf_class);
+    }
+    return false;
+  }
+#endif
+
   if (header_.e_ident[EI_DATA] != ELFDATA2LSB) {
     DL_ERR("\"%s\" not little-endian: %d", name_, header_.e_ident[EI_DATA]);
     return false;
@@ -189,6 +208,8 @@
       EM_MIPS
 #elif defined(ANDROID_X86_LINKER)
       EM_386
+#elif defined(ANDROID_X86_64_LINKER)
+      EM_X86_64
 #endif
   ) {
     DL_ERR("\"%s\" has unexpected e_machine: %d", name_, header_.e_machine);
@@ -291,7 +312,7 @@
   int mmap_flags = MAP_PRIVATE | MAP_ANONYMOUS;
   void* start = mmap(addr, load_size_, PROT_NONE, mmap_flags, -1, 0);
   if (start == MAP_FAILED) {
-    DL_ERR("couldn't reserve %d bytes of address space for \"%s\"", load_size_, name_);
+    DL_ERR("couldn't reserve %zd bytes of address space for \"%s\"", load_size_, name_);
     return false;
   }
 
@@ -620,6 +641,6 @@
       return true;
     }
   }
-  DL_ERR("\"%s\" loaded phdr %x not in loadable segment", name_, loaded);
+  DL_ERR("\"%s\" loaded phdr %p not in loadable segment", name_, reinterpret_cast<void*>(loaded));
   return false;
 }
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index 28d8b39..f774b5d 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -46,7 +46,7 @@
 
   size_t phdr_count() { return phdr_num_; }
   Elf_Addr load_start() { return reinterpret_cast<Elf_Addr>(load_start_); }
-  Elf_Addr load_size() { return load_size_; }
+  size_t load_size() { return load_size_; }
   Elf_Addr load_bias() { return load_bias_; }
   const Elf_Phdr* loaded_phdr() { return loaded_phdr_; }
 
@@ -72,7 +72,7 @@
   // First page of reserved address space.
   void* load_start_;
   // Size in bytes of reserved address space.
-  Elf_Addr load_size_;
+  size_t load_size_;
   // Load bias.
   Elf_Addr load_bias_;