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
diff --git a/linker/linker.cpp b/linker/linker.cpp
index edaa6f4..6d38632 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -4037,6 +4037,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
@@ -4083,7 +4097,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?");
   }
@@ -4096,7 +4116,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 109bab5..09f7d66 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -803,7 +803,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;
@@ -905,7 +905,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 748d0ca..56df1a6 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -805,15 +805,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");
@@ -838,6 +835,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