Commit dcaef371 authored by Dimitry Ivanov's avatar Dimitry Ivanov
Browse files

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 2ba1cf39)
parent d120719e
......@@ -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);
......
......@@ -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
......
......@@ -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
......
......@@ -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]);
......
......@@ -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
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment