aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDimitry Ivanov <dimitry@google.com>2016-05-17 13:29:37 -0700
committerDimitry Ivanov <dimitry@google.com>2016-05-17 15:31:05 -0700
commitdcaef3710df817db5652a1f3ab4646f43f5cd3ee (patch)
tree3aa1bd5e9f1c8040189ef2abb01228938e2fd9d9
parentd120719e250d3f112b1c71ae775cab09c70191cd (diff)
downloadbionic-dcaef3710df817db5652a1f3ab4646f43f5cd3ee.tar.gz
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)
-rw-r--r--linker/linker.cpp24
-rw-r--r--tests/dlext_test.cpp4
-rw-r--r--tests/dlfcn_test.cpp23
-rw-r--r--tests/gtest_main.cpp30
-rw-r--r--tests/utils.h3
5 files changed, 61 insertions, 23 deletions
diff --git a/linker/linker.cpp b/linker/linker.cpp
index d21625edb..799ec50ef 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -4142,6 +4142,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
@@ -4188,7 +4202,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?");
}
@@ -4201,7 +4221,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);
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp
index 205ad410f..7dd82f312 100644
--- a/tests/dlext_test.cpp
+++ b/tests/dlext_test.cpp
@@ -799,7 +799,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;
@@ -901,7 +901,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 81479d51b..f842c66ca 100644
--- a/tests/dlfcn_test.cpp
+++ b/tests/dlfcn_test.cpp
@@ -790,15 +790,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");
@@ -823,6 +820,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<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]);
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