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/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