Fix dlopen of main executable by absolute path

This CL adds initialization of inode for the main executable
which enables linker to resolve the correct soinfo when
application calls dlopen with absolute path to the
main executable.

Bug: http://b/28420266
Change-Id: I102e07bde454bd44c6e46075e3faeeb5092830d8
(cherry picked from commit 2ba1cf39ae6087249a839ec7b3793d4d4fa75438)
diff --git a/linker/linker.cpp b/linker/linker.cpp
index d21625e..799ec50 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -4142,6 +4142,20 @@
 
 extern "C" int __system_properties_init(void);
 
+static const char* get_executable_path() {
+  static std::string executable_path;
+  if (executable_path.empty()) {
+    char path[PATH_MAX];
+    ssize_t path_len = readlink("/proc/self/exe", path, sizeof(path));
+    if (path_len == -1 || path_len >= static_cast<ssize_t>(sizeof(path))) {
+      __libc_fatal("readlink('/proc/self/exe') failed: %s", strerror(errno));
+    }
+    executable_path = std::string(path, path_len);
+  }
+
+  return executable_path.c_str();
+}
+
 /*
  * This code is called after the linker has linked itself and
  * fixed it's own GOT. It is safe to make references to externs
@@ -4188,7 +4202,13 @@
     }
   }
 
-  soinfo* si = soinfo_alloc(&g_default_namespace, args.argv[0], nullptr, 0, RTLD_GLOBAL);
+  const char* executable_path = get_executable_path();
+  struct stat file_stat;
+  if (TEMP_FAILURE_RETRY(stat(executable_path, &file_stat)) != 0) {
+    __libc_fatal("unable to stat file for the executable \"%s\": %s", executable_path, strerror(errno));
+  }
+
+  soinfo* si = soinfo_alloc(&g_default_namespace, executable_path, &file_stat, 0, RTLD_GLOBAL);
   if (si == nullptr) {
     __libc_fatal("Couldn't allocate soinfo: out of memory?");
   }
@@ -4201,7 +4221,7 @@
   // gdb aware of them before loading the rest of the dependency
   // tree.
   map->l_addr = 0;
-  map->l_name = args.argv[0];
+  map->l_name = const_cast<char*>(executable_path);
   insert_link_map_into_debug_map(map);
   init_linker_info_for_gdb(linker_base);
 
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 205ad41..7dd82f3 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -799,7 +799,7 @@
   handle2 = android_dlopen_ext(lib_private_external_path.c_str(), RTLD_NOW, &extinfo);
   ASSERT_TRUE(handle2 == nullptr);
   ASSERT_EQ("dlopen failed: library \"" + lib_private_external_path + "\" needed"
-            " or dlopened by \"" + get_executable_name() +  "\" is not accessible"
+            " or dlopened by \"" + get_executable_path() +  "\" is not accessible"
             " for the namespace \"private_isolated1\"", dlerror());
 
   extinfo.library_namespace = ns_isolated2;
@@ -901,7 +901,7 @@
   handle2 = android_dlopen_ext(lib_private_external_path.c_str(), RTLD_NOW, &extinfo);
   ASSERT_TRUE(handle2 == nullptr);
   ASSERT_EQ("dlopen failed: library \"" + lib_private_external_path + "\" needed"
-            " or dlopened by \"" + get_executable_name() + "\" is not accessible"
+            " or dlopened by \"" + get_executable_path() + "\" is not accessible"
             " for the namespace \"private_isolated_shared\"", dlerror());
 
   // load libnstest_root.so to shared namespace in order to check that everything is different
diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp
index 81479d5..f842c66 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -790,15 +790,12 @@
   ASSERT_NE(rc, 0); // Zero on error, non-zero on success.
 
   // Get the name of this executable.
-  char executable_path[PATH_MAX];
-  rc = readlink("/proc/self/exe", executable_path, sizeof(executable_path));
-  ASSERT_NE(rc, -1);
-  executable_path[rc] = '\0';
+  const std::string& executable_path = get_executable_path();
 
   // The filename should be that of this executable.
   char dli_realpath[PATH_MAX];
   ASSERT_TRUE(realpath(info.dli_fname, dli_realpath) != nullptr);
-  ASSERT_STREQ(executable_path, dli_realpath);
+  ASSERT_STREQ(executable_path.c_str(), dli_realpath);
 
   // The symbol name should be the symbol we looked up.
   ASSERT_STREQ(info.dli_sname, "DlSymTestFunction");
@@ -823,6 +820,22 @@
   ASSERT_EQ(0, dlclose(self));
 }
 
+TEST(dlfcn, dlopen_executable_by_absolute_path) {
+  void* handle1 = dlopen(nullptr, RTLD_NOW);
+  ASSERT_TRUE(handle1 != nullptr) << dlerror();
+
+  void* handle2 = dlopen(get_executable_path().c_str(), RTLD_NOW);
+  ASSERT_TRUE(handle2 != nullptr) << dlerror();
+
+#if defined(__BIONIC__)
+  ASSERT_EQ(handle1, handle2);
+#else
+  GTEST_LOG_(INFO) << "Skipping ASSERT_EQ(handle1, handle2) for glibc: "
+                      "it loads a separate copy of the main executable "
+                      "on dlopen by absolute path.";
+#endif
+}
+
 #if defined(__LP64__)
 #define PATH_TO_SYSTEM_LIB "/system/lib64/"
 #else
diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp
index ad23aa8..2b58646 100644
--- a/tests/gtest_main.cpp
+++ b/tests/gtest_main.cpp
@@ -46,10 +46,10 @@
 
 #endif
 
-static std::string g_executable_name;
+static std::string g_executable_path;
 
-const std::string& get_executable_name() {
-  return g_executable_name;
+const std::string& get_executable_path() {
+  return g_executable_path;
 }
 
 namespace testing {
@@ -923,15 +923,8 @@
   // The reason is that gtest uses clone() + execve() to run DeathTest in threadsafe mode,
   // and execve() doesn't read environment variable PATH, so execve() will not success
   // until we specify the absolute path or relative path of the test program directly.
-  if (strchr(args[0], '/') == NULL) {
-    char path[PATH_MAX];
-    ssize_t path_len = readlink("/proc/self/exe", path, sizeof(path));
-    if (path_len <= 0 || path_len >= static_cast<ssize_t>(sizeof(path))) {
-      perror("readlink");
-      exit(1);
-    }
-    path[path_len] = '\0';
-    args[0] = strdup(path);
+  if (strchr(args[0], '/') == nullptr) {
+    args[0] = strdup(g_executable_path.c_str());
   }
 }
 
@@ -1118,8 +1111,19 @@
   return true;
 }
 
+static std::string get_proc_self_exe() {
+  char path[PATH_MAX];
+  ssize_t path_len = readlink("/proc/self/exe", path, sizeof(path));
+  if (path_len <= 0 || path_len >= static_cast<ssize_t>(sizeof(path))) {
+    perror("readlink");
+    exit(1);
+  }
+
+  return std::string(path, path_len);
+}
+
 int main(int argc, char** argv) {
-  g_executable_name = argv[0];
+  g_executable_path = get_proc_self_exe();
   std::vector<char*> arg_list;
   for (int i = 0; i < argc; ++i) {
     arg_list.push_back(argv[i]);
diff --git a/tests/utils.h b/tests/utils.h
index a335c66..f8e0039 100644
--- a/tests/utils.h
+++ b/tests/utils.h
@@ -119,6 +119,7 @@
   ASSERT_EQ(expected_exit_status, WEXITSTATUS(status));
 }
 
-const std::string& get_executable_name();
+// The absolute path to the executable
+const std::string& get_executable_path();
 
 #endif