From: Dimitry Ivanov Date: Tue, 17 May 2016 20:29:37 +0000 (-0700) Subject: Fix dlopen of main executable by absolute path X-Git-Tag: android-x86-8.1-r1~224^2^2~85^2~127^2~157^2 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=2ba1cf39ae6087249a839ec7b3793d4d4fa75438;p=android-x86%2Fbionic.git 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 edaa6f459..6d3863213 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -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(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(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 109bab5a8..09f7d668a 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp @@ -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 diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 748d0cab4..56df1a660 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -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 diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp index ad23aa80c..2b5864688 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 @@ static void AddPathSeparatorInTestProgramPath(std::vector& 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(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& 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(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 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 a335c6663..f8e003922 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -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