AArch64: Linker64 support for AArch64

Addition of support for AArch64 in the linker64 target.

Change-Id: I8dfd9711278f6706063e91f626b6007ea7a3dd6e
Signed-off-by: Marcus Oakland <marcus.oakland@arm.com>
diff --git a/libc/arch-x86_64/include/machine/exec.h b/libc/arch-x86_64/include/machine/exec.h
index 6d16439..829351c 100644
--- a/libc/arch-x86_64/include/machine/exec.h
+++ b/libc/arch-x86_64/include/machine/exec.h
@@ -12,7 +12,7 @@
 
 #define ELF_TARG_CLASS		ELFCLASS64
 #define ELF_TARG_DATA		ELFDATA2LSB
-#define ELF_TARG_MACH		EM_AMD64
+#define ELF_TARG_MACH		EM_X86_64
 
 #define _NLIST_DO_ELF
 #define _KERN_DO_ELF64
diff --git a/linker/Android.mk b/linker/Android.mk
index f73d8d6..1bf3e9d 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -33,7 +33,7 @@
 # We need to access Bionic private headers in the linker.
 LOCAL_CFLAGS += -I$(LOCAL_PATH)/../libc/
 
-ifeq ($(TARGET_ARCH),$(filter $(TARGET_ARCH),x86_64))
+ifeq ($(TARGET_IS_64_BIT),true)
     LOCAL_MODULE := linker64
 else
     LOCAL_MODULE := linker
diff --git a/linker/arch/aarch64/begin.S b/linker/arch/aarch64/begin.S
new file mode 100644
index 0000000..55618b7
--- /dev/null
+++ b/linker/arch/aarch64/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)
+  mov x0, sp
+  mov x1, xzr
+  bl  __linker_init
+
+  /* linker init returns the _entry address in the main image */
+  br x0
+END(_start)
diff --git a/linker/debugger.cpp b/linker/debugger.cpp
index 29afab1..92e9dac 100644
--- a/linker/debugger.cpp
+++ b/linker/debugger.cpp
@@ -92,7 +92,7 @@
         return -1;
     }
 
-    int err = TEMP_FAILURE_RETRY(connect(s, (sockaddr*) &addr, alen));
+    int err = TEMP_FAILURE_RETRY(connect(s, reinterpret_cast<sockaddr*>(&addr), alen));
     if (err == -1) {
         close(s);
         s = -1;
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index 09f3ddf..207e03c 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -148,13 +148,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(__i386__) || defined(__mips__) || defined(__x86_64__)
+#elif defined(__aarch64__) || defined(__i386__) || defined(__mips__) || defined(__x86_64__)
 //   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, x86, and x86_64 are presently supported.
+#error Unsupported architecture. Only aarch64, arm, mips, x86, and x86_64 are presently supported.
 #endif
 
 // name_offset: starting index of the name in libdl_info.strtab
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 6ebba8e..81ca2f5 100755
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -31,6 +31,7 @@
 #include <fcntl.h>
 #include <linux/auxvec.h>
 #include <pthread.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -138,10 +139,18 @@
 
 #if COUNT_PAGES
 static unsigned bitmask[4096];
+#if defined(__LP64__)
+#define MARK(offset) \
+    do { \
+        if ((((offset) >> 12) >> 5) < 4096) \
+            bitmask[((offset) >> 12) >> 5] |= (1 << (((offset) >> 12) & 31)); \
+    } while(0)
+#else
 #define MARK(offset) \
     do { \
         bitmask[((offset) >> 12) >> 3] |= (1 << (((offset) >> 12) & 7)); \
     } while(0)
+#endif
 #else
 #define MARK(x) do {} while (0)
 #endif
@@ -889,7 +898,20 @@
          */
 
         switch (type) {
-#if defined(__x86_64__)
+#if defined(__aarch64__)
+        case R_AARCH64_JUMP_SLOT:
+        case R_AARCH64_GLOB_DAT:
+        case R_AARCH64_ABS64:
+        case R_AARCH64_ABS32:
+        case R_AARCH64_ABS16:
+        case R_AARCH64_RELATIVE:
+          /*
+           * The sym_addr was initialized to be zero above, or the relocation
+           * code below does not care about value of sym_addr.
+           * No need to do anything.
+           */
+          break;
+#elif defined(__x86_64__)
         case R_X86_64_JUMP_SLOT:
         case R_X86_64_GLOB_DAT:
         case R_X86_64_32:
@@ -900,7 +922,6 @@
           sym_addr = reloc;
           break;
 #endif
-
         default:
           DL_ERR("unknown weak reloc type %d @ %p (%d)", type, rela, (int) (rela - start));
           return -1;
@@ -915,7 +936,197 @@
     }
 
     switch (type) {
-#if defined(__x86_64__)
+#if defined(__aarch64__)
+    case R_AARCH64_JUMP_SLOT:
+        count_relocation(kRelocAbsolute);
+        MARK(rela->r_offset);
+        TRACE_TYPE(RELO, "RELO JMP_SLOT %16lx <- %16lx %s\n",
+                    reloc,
+                    (sym_addr + rela->r_addend),
+                    sym_name);
+        *reinterpret_cast<Elf_Addr*>(reloc) = (sym_addr + rela->r_addend);
+        break;
+    case R_AARCH64_GLOB_DAT:
+        count_relocation(kRelocAbsolute);
+        MARK(rela->r_offset);
+        TRACE_TYPE(RELO, "RELO GLOB_DAT %16lx <- %16lx %s\n",
+                    reloc,
+                    (sym_addr + rela->r_addend),
+                    sym_name);
+        *reinterpret_cast<Elf_Addr*>(reloc) = (sym_addr + rela->r_addend);
+        break;
+    case R_AARCH64_ABS64:
+        count_relocation(kRelocAbsolute);
+        MARK(rela->r_offset);
+        TRACE_TYPE(RELO, "RELO ABS64 %16lx <- %16lx %s\n",
+                    reloc,
+                    (sym_addr + rela->r_addend),
+                    sym_name);
+        *reinterpret_cast<Elf_Addr*>(reloc) += (sym_addr + rela->r_addend);
+        break;
+    case R_AARCH64_ABS32:
+        count_relocation(kRelocAbsolute);
+        MARK(rela->r_offset);
+        TRACE_TYPE(RELO, "RELO ABS32 %16lx <- %16lx %s\n",
+                    reloc,
+                    (sym_addr + rela->r_addend),
+                    sym_name);
+        if ((static_cast<Elf_Addr>(INT32_MIN) <=
+          (*reinterpret_cast<Elf_Addr*>(reloc) + (sym_addr + rela->r_addend))) &&
+          ((*reinterpret_cast<Elf_Addr*>(reloc) + (sym_addr + rela->r_addend)) <=
+          static_cast<Elf_Addr>(UINT32_MAX))) {
+            *reinterpret_cast<Elf_Addr*>(reloc) += (sym_addr + rela->r_addend);
+        } else {
+            DL_ERR("0x%016lx out of range 0x%016lx to 0x%016lx",
+                    (*reinterpret_cast<Elf_Addr*>(reloc) + (sym_addr + rela->r_addend)),
+                    static_cast<Elf_Addr>(INT32_MIN),
+                    static_cast<Elf_Addr>(UINT32_MAX));
+            return -1;
+        }
+        break;
+    case R_AARCH64_ABS16:
+        count_relocation(kRelocAbsolute);
+        MARK(rela->r_offset);
+        TRACE_TYPE(RELO, "RELO ABS16 %16lx <- %16lx %s\n",
+                    reloc,
+                    (sym_addr + rela->r_addend),
+                    sym_name);
+        if ((static_cast<Elf_Addr>(INT16_MIN) <=
+          (*reinterpret_cast<Elf_Addr*>(reloc) + (sym_addr + rela->r_addend))) &&
+          ((*reinterpret_cast<Elf_Addr*>(reloc) + (sym_addr + rela->r_addend)) <=
+          static_cast<Elf_Addr>(UINT16_MAX))) {
+            *reinterpret_cast<Elf_Addr*>(reloc) += (sym_addr + rela->r_addend);
+        } else {
+            DL_ERR("0x%016lx out of range 0x%016lx to 0x%016lx",
+                    (*reinterpret_cast<Elf_Addr*>(reloc) + (sym_addr + rela->r_addend)),
+                    static_cast<Elf_Addr>(INT16_MIN),
+                    static_cast<Elf_Addr>(UINT16_MAX));
+            return -1;
+        }
+        break;
+    case R_AARCH64_PREL64:
+        count_relocation(kRelocRelative);
+        MARK(rela->r_offset);
+        TRACE_TYPE(RELO, "RELO REL64 %16lx <- %16lx - %16lx %s\n",
+                    reloc,
+                    (sym_addr + rela->r_addend),
+                    rela->r_offset,
+                    sym_name);
+        *reinterpret_cast<Elf_Addr*>(reloc) += (sym_addr + rela->r_addend) - rela->r_offset;
+        break;
+    case R_AARCH64_PREL32:
+        count_relocation(kRelocRelative);
+        MARK(rela->r_offset);
+        TRACE_TYPE(RELO, "RELO REL32 %16lx <- %16lx - %16lx %s\n",
+                    reloc,
+                    (sym_addr + rela->r_addend),
+                    rela->r_offset, sym_name);
+        if ((static_cast<Elf_Addr>(INT32_MIN) <=
+          (*reinterpret_cast<Elf_Addr*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset))) &&
+          ((*reinterpret_cast<Elf_Addr*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset)) <=
+          static_cast<Elf_Addr>(UINT32_MAX))) {
+            *reinterpret_cast<Elf_Addr*>(reloc) += ((sym_addr + rela->r_addend) - rela->r_offset);
+        } else {
+            DL_ERR("0x%016lx out of range 0x%016lx to 0x%016lx",
+                    (*reinterpret_cast<Elf_Addr*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset)),
+                    static_cast<Elf_Addr>(INT32_MIN),
+                    static_cast<Elf_Addr>(UINT32_MAX));
+            return -1;
+        }
+        break;
+    case R_AARCH64_PREL16:
+        count_relocation(kRelocRelative);
+        MARK(rela->r_offset);
+        TRACE_TYPE(RELO, "RELO REL16 %16lx <- %16lx - %16lx %s\n",
+                    reloc,
+                    (sym_addr + rela->r_addend),
+                    rela->r_offset, sym_name);
+        if ((static_cast<Elf_Addr>(INT16_MIN) <=
+          (*reinterpret_cast<Elf_Addr*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset))) &&
+          ((*reinterpret_cast<Elf_Addr*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset)) <=
+          static_cast<Elf_Addr>(UINT16_MAX))) {
+            *reinterpret_cast<Elf_Addr*>(reloc) += ((sym_addr + rela->r_addend) - rela->r_offset);
+        } else {
+            DL_ERR("0x%016lx out of range 0x%016lx to 0x%016lx",
+                    (*reinterpret_cast<Elf_Addr*>(reloc) + ((sym_addr + rela->r_addend) - rela->r_offset)),
+                    static_cast<Elf_Addr>(INT16_MIN),
+                    static_cast<Elf_Addr>(UINT16_MAX));
+            return -1;
+        }
+        break;
+
+    case R_AARCH64_RELATIVE:
+        count_relocation(kRelocRelative);
+        MARK(rela->r_offset);
+        if (sym) {
+            DL_ERR("odd RELATIVE form...");
+            return -1;
+        }
+        TRACE_TYPE(RELO, "RELO RELATIVE %16lx <- %16lx\n",
+                    reloc,
+                    (si->base + rela->r_addend));
+        *reinterpret_cast<Elf_Addr*>(reloc) = (si->base + rela->r_addend);
+        break;
+
+    case R_AARCH64_COPY:
+        if ((si->flags & FLAG_EXE) == 0) {
+            /*
+              * http://infocenter.arm.com/help/topic/com.arm.doc.ihi0044d/IHI0044D_aaelf.pdf
+              *
+              * Section 4.7.1.10 "Dynamic relocations"
+              * R_AARCH64_COPY may only appear in executable objects where e_type is
+              * set to ET_EXEC.
+              *
+              * FLAG_EXE is set for both ET_DYN and ET_EXEC executables.
+              * We should explicitly disallow ET_DYN executables from having
+              * R_AARCH64_COPY relocations.
+              */
+            DL_ERR("%s R_AARCH64_COPY relocations only supported for ET_EXEC", si->name);
+            return -1;
+        }
+        count_relocation(kRelocCopy);
+        MARK(rela->r_offset);
+        TRACE_TYPE(RELO, "RELO COPY %16lx <- %ld @ %16lx %s\n",
+                    reloc,
+                    s->st_size,
+                    (sym_addr + rela->r_addend),
+                    sym_name);
+        if (reloc == (sym_addr + rela->r_addend)) {
+            Elf_Sym *src = soinfo_do_lookup(NULL, sym_name, &lsi, needed);
+
+            if (src == NULL) {
+                DL_ERR("%s R_AARCH64_COPY relocation source cannot be resolved", si->name);
+                return -1;
+            }
+            if (lsi->has_DT_SYMBOLIC) {
+                DL_ERR("%s invalid R_AARCH64_COPY relocation against DT_SYMBOLIC shared "
+                        "library %s (built with -Bsymbolic?)", si->name, lsi->name);
+                return -1;
+            }
+            if (s->st_size < src->st_size) {
+                DL_ERR("%s R_AARCH64_COPY relocation size mismatch (%ld < %ld)",
+                        si->name, s->st_size, src->st_size);
+                return -1;
+            }
+            memcpy((void*)reloc, (void*)(src->st_value + lsi->load_bias), src->st_size);
+        } else {
+            DL_ERR("%s R_AARCH64_COPY relocation target cannot be resolved", si->name);
+            return -1;
+        }
+        break;
+    case R_AARCH64_TLS_TPREL64:
+        TRACE_TYPE(RELO, "RELO TLS_TPREL64 *** %16lx <- %16lx - %16lx\n",
+                    reloc,
+                    (sym_addr + rela->r_addend),
+                    rela->r_offset);
+        break;
+    case R_AARCH64_TLS_DTPREL32:
+        TRACE_TYPE(RELO, "RELO TLS_DTPREL32 *** %16lx <- %16lx - %16lx\n",
+                    reloc,
+                    (sym_addr + rela->r_addend),
+                    rela->r_offset);
+        break;
+#elif defined(__x86_64__)
     case R_X86_64_JUMP_SLOT:
       count_relocation(kRelocAbsolute);
       MARK(rela->r_offset);
@@ -964,6 +1175,7 @@
       *reinterpret_cast<Elf_Addr*>(reloc) = sym_addr + rela->r_addend - reloc;
       break;
 #endif
+
     default:
       DL_ERR("unknown reloc type %d @ %p (%d)", type, rela, (int) (rela - start));
       return -1;
@@ -1934,7 +2146,11 @@
         for (n = 0; n < 4096; n++) {
             if (bitmask[n]) {
                 unsigned x = bitmask[n];
+#if defined(__LP64__)
+                for (i = 0; i < 32; i++) {
+#else
                 for (i = 0; i < 8; i++) {
+#endif
                     if (x & 1) {
                         count++;
                     }
@@ -1990,7 +2206,6 @@
   KernelArgumentBlock args(raw_args);
 
   Elf_Addr linker_addr = args.getauxval(AT_BASE);
-
   Elf_Ehdr* elf_hdr = reinterpret_cast<Elf_Ehdr*>(linker_addr);
   Elf_Phdr* phdr = (Elf_Phdr*)((unsigned char*) linker_addr + elf_hdr->e_phoff);
 
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 4884364..b4d72b2 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -29,6 +29,7 @@
 #include "linker_phdr.h"
 
 #include <errno.h>
+#include <machine/exec.h>
 #include <sys/mman.h>
 
 #include "linker.h"
@@ -201,17 +202,7 @@
     return false;
   }
 
-  if (header_.e_machine !=
-#if defined(__arm__)
-      EM_ARM
-#elif defined(__i386__)
-      EM_386
-#elif defined(__mips__)
-      EM_MIPS
-#elif defined(__x86_64__)
-      EM_X86_64
-#endif
-  ) {
+  if (header_.e_machine != ELF_TARG_MACH) {
     DL_ERR("\"%s\" has unexpected e_machine: %d", name_, header_.e_machine);
     return false;
   }
diff --git a/tests/Android.mk b/tests/Android.mk
index d4d82ee..a342be7 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -32,9 +32,6 @@
 ifeq ($(TARGET_ARCH),aarch64)
   $(info TODO: $(LOCAL_PATH)/Android.mk -fstack-protector not yet available for the AArch64 toolchain)
   test_c_flags += -fno-stack-protector
-
-  $(info TODO: $(LOCAL_PATH)/Android.mk aarch64 GCC sees things other GCCs do not; punt for now)
-  test_c_flags += -Wno-error=strict-aliasing
 endif # aarch64
 
 test_src_files = \