A special linker for ASan executables.

Setup a /system/bin/linker_asan as a symlink to "linker".
Read the linker name from PT_INTERP, and if it is linker_asan,
switch default library lookup paths to the ASan set, which
starts with the path to the instrumented libraries
(/data/lib), followed by /system/lib as a fallback.

This ensures that ASan binaries prefer ASan libraries, when
available. This approach is way better then RPATH/RUNPATH and even
better than LD_LIBRARY_PATH:
- RUNPATH is per-DSO, while default paths are global.
- LD_LIBRARY_PATH is overwritten by android_update_LD_LIBRARY_PATH.
- neither RUNPATH nor LD_LIBRARY_PATH appear in
  android_get_LD_LIBRARY_PATH which is used to build java.lang.path.
  Having ASan libraries in java.lang.path is a good thing.

Bug: 22355945
Change-Id: I1d2791fbf5740618f18f71a3ae3d873714669d3f
diff --git a/linker/Android.mk b/linker/Android.mk
index 7a9b5d9..761ce8f 100644
--- a/linker/Android.mk
+++ b/linker/Android.mk
@@ -83,4 +83,26 @@
 
 include $(BUILD_EXECUTABLE)
 
+
+ifeq (address, $(strip $(SANITIZE_TARGET)))
+
+define add-linker-symlink
+$(eval _from := $(TARGET_OUT)/bin/$(1))
+$(eval _to:=$(2))
+$(_from): $(LOCAL_MODULE_MAKEFILE)
+        @echo "Symlink: $$@ -> $(_to)"
+        @mkdir -p $$(dir $$@)
+        @rm -rf $$@
+        $(hide) ln -sf $(_to) $$@
+ALL_MODULES.linker.INSTALLED += $(_from)
+linker: $(_from)
+endef
+
+$(eval $(call add-linker-symlink,linker_asan,linker))
+ifeq ($(TARGET_IS_64_BIT),true)
+$(eval $(call add-linker-symlink,linker_asan64,linker64))
+endif
+ALL_MODULES += linker
+endif
+
 include $(call first-makefiles-under,$(LOCAL_PATH))
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 402e6ba..97e390b 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -85,9 +85,25 @@
   nullptr
 };
 
+static const char* const kAsanDefaultLdPaths[] = {
+#if defined(__LP64__)
+  "/data/vendor/lib64",
+  "/vendor/lib64",
+  "/data/lib64",
+  "/system/lib64",
+#else
+  "/data/vendor/lib",
+  "/vendor/lib",
+  "/data/lib",
+  "/system/lib",
+#endif
+  nullptr
+};
+
 static const ElfW(Versym) kVersymNotNeeded = 0;
 static const ElfW(Versym) kVersymGlobal = 1;
 
+static const char* const* g_default_ld_paths;
 static std::vector<std::string> g_ld_library_paths;
 static std::vector<std::string> g_ld_preload_names;
 
@@ -1186,9 +1202,9 @@
 }
 
 static int open_library_on_default_path(const char* name, off64_t* file_offset) {
-  for (size_t i = 0; kDefaultLdPaths[i] != nullptr; ++i) {
+  for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) {
     char buf[512];
-    if (!format_path(buf, sizeof(buf), kDefaultLdPaths[i], name)) {
+    if (!format_path(buf, sizeof(buf), g_default_ld_paths[i], name)) {
       continue;
     }
 
@@ -1706,14 +1722,19 @@
   // See b/17302493 for further details.
   // Once the above bug is fixed, this code can be modified to use
   // snprintf again.
-  size_t required_len = strlen(kDefaultLdPaths[0]) + strlen(kDefaultLdPaths[1]) + 2;
+  size_t required_len = 0;
+  for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) {
+    required_len += strlen(g_default_ld_paths[i]) + 1;
+  }
   if (buffer_size < required_len) {
     __libc_fatal("android_get_LD_LIBRARY_PATH failed, buffer too small: "
                  "buffer len %zu, required len %zu", buffer_size, required_len);
   }
-  char* end = stpcpy(buffer, kDefaultLdPaths[0]);
-  *end = ':';
-  strcpy(end + 1, kDefaultLdPaths[1]);
+  char* end = buffer;
+  for (size_t i = 0; g_default_ld_paths[i] != nullptr; ++i) {
+    if (i > 0) *end++ = ':';
+    end = stpcpy(end, g_default_ld_paths[i]);
+  }
 }
 
 void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path) {
@@ -3156,6 +3177,16 @@
   insert_soinfo_into_debug_map(linker_soinfo_for_gdb);
 }
 
+static void init_default_ld_library_path() {
+  const char *interp = phdr_table_get_interpreter_name(somain->phdr, somain->phnum,
+                                                       somain->load_bias);
+  const char* bname = basename(interp);
+  if (bname && (strcmp(bname, "linker_asan") == 0 || strcmp(bname, "linker_asan64") == 0))
+    g_default_ld_paths = kAsanDefaultLdPaths;
+  else
+    g_default_ld_paths = kDefaultLdPaths;
+};
+
 extern "C" int __system_properties_init(void);
 
 /*
@@ -3246,6 +3277,8 @@
 
   somain = si;
 
+  init_default_ld_library_path();
+
   if (!si->prelink_image()) {
     __libc_format_fd(2, "CANNOT LINK EXECUTABLE: %s\n", linker_get_error_buffer());
     exit(EXIT_FAILURE);
diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp
index 30118e3..cf012f4 100644
--- a/linker/linker_phdr.cpp
+++ b/linker/linker_phdr.cpp
@@ -771,6 +771,26 @@
   }
 }
 
+/* Return the program interpreter string, or nullptr if missing.
+ *
+ * Input:
+ *   phdr_table  -> program header table
+ *   phdr_count  -> number of entries in tables
+ *   load_bias   -> load bias
+ * Return:
+ *   pointer to the program interpreter string.
+ */
+const char* phdr_table_get_interpreter_name(const ElfW(Phdr) * phdr_table, size_t phdr_count,
+                                            ElfW(Addr) load_bias) {
+  for (size_t i = 0; i<phdr_count; ++i) {
+    const ElfW(Phdr)& phdr = phdr_table[i];
+    if (phdr.p_type == PT_INTERP) {
+      return reinterpret_cast<const char*>(load_bias + phdr.p_vaddr);
+    }
+  }
+  return nullptr;
+}
+
 // Sets loaded_phdr_ to the address of the program header table as it appears
 // in the loaded segments in memory. This is in contrast with phdr_table_,
 // which is temporary and will be released before the library is relocated.
diff --git a/linker/linker_phdr.h b/linker/linker_phdr.h
index 3affa66..55196fd 100644
--- a/linker/linker_phdr.h
+++ b/linker/linker_phdr.h
@@ -109,4 +109,7 @@
                                     ElfW(Addr) load_bias, ElfW(Dyn)** dynamic,
                                     ElfW(Word)* dynamic_flags);
 
+const char* phdr_table_get_interpreter_name(const ElfW(Phdr) * phdr_table, size_t phdr_count,
+                                            ElfW(Addr) load_bias);
+
 #endif /* LINKER_PHDR_H */