OSDN Git Service

Fix dlopen of main executable by absolute path
authorDimitry Ivanov <dimitry@google.com>
Tue, 17 May 2016 20:29:37 +0000 (13:29 -0700)
committerDimitry Ivanov <dimitry@google.com>
Tue, 17 May 2016 20:55:46 +0000 (13:55 -0700)
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

linker/linker.cpp
tests/dlext_test.cpp
tests/dlfcn_test.cpp
tests/gtest_main.cpp
tests/utils.h

index edaa6f4..6d38632 100644 (file)
@@ -4037,6 +4037,20 @@ static void init_default_namespace() {
 
 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 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(
     }
   }
 
-  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 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW(
   // 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);
 
index 109bab5..09f7d66 100644 (file)
@@ -803,7 +803,7 @@ TEST(dlext, ns_isolated) {
   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 @@ TEST(dlext, ns_shared) {
   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
index 748d0ca..56df1a6 100644 (file)
@@ -805,15 +805,12 @@ TEST(dlfcn, dladdr_executable) {
   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 @@ TEST(dlfcn, dladdr_executable) {
   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
index ad23aa8..2b58646 100644 (file)
 
 #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 @@ static void AddPathSeparatorInTestProgramPath(std::vector<char*>& args) {
   // 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 @@ static bool PickOptions(std::vector<char*>& args, IsolationTestOptions& options)
   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]);
index a335c66..f8e0039 100644 (file)
@@ -119,6 +119,7 @@ static inline void AssertChildExited(int pid, int expected_exit_status) {
   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