From 554374693408cd7c74d0cae596fca7349661edea Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Wed, 20 Jul 2016 15:33:07 -0700 Subject: [PATCH] Pass argc/argv/envp to dso constructors The glibc implementation of the loader passes argc/argv/envp to all elf constructors. This change makes bionic linker behave in the same way. Bug: http://b/30145768 Change-Id: I1c65c42aa5305a5b133c64b5748205bbde869e0e Test: run bionic-unit-tests --gtest_filter=dl*:Dl* --- linker/linker.cpp | 96 ++++++++++++++++++++++++++++++------------------ linker/linker.h | 15 ++++---- linker/linker_logger.cpp | 2 +- linker/linker_logger.h | 2 +- tests/dlfcn_test.cpp | 13 ++++++- tests/gtest_main.cpp | 20 +++++++++- tests/utils.h | 5 +++ 7 files changed, 104 insertions(+), 49 deletions(-) diff --git a/linker/linker.cpp b/linker/linker.cpp index 4e8a33c0d..71e3774af 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -195,7 +195,11 @@ static soinfo::soinfo_list_t g_public_namespace; int g_ld_debug_verbosity; abort_msg_t* g_abort_message = nullptr; // For debuggerd. -const char* g_argv0 = nullptr; + +// These values are used to call constructors for .init_array && .preinit_array +int g_argc = 0; +char** g_argv = nullptr; +char** g_envp = nullptr; static std::string dirname(const char *path) { const char* last_slash = strrchr(path, '/'); @@ -3020,13 +3024,41 @@ bool soinfo::relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& r } #endif // !defined(__mips__) -void soinfo::call_array(const char* array_name __unused, linker_function_t* functions, - size_t count, bool reverse) { +static void call_function(const char* function_name __unused, + linker_ctor_function_t function, + const char* realpath __unused) { + if (function == nullptr || reinterpret_cast(function) == static_cast(-1)) { + return; + } + + TRACE("[ Calling c-tor %s @ %p for '%s' ]", function_name, function, realpath); + function(g_argc, g_argv, g_envp); + TRACE("[ Done calling c-tor %s @ %p for '%s' ]", function_name, function, realpath); +} + +static void call_function(const char* function_name __unused, + linker_dtor_function_t function, + const char* realpath __unused) { + if (function == nullptr || reinterpret_cast(function) == static_cast(-1)) { + return; + } + + TRACE("[ Calling d-tor %s @ %p for '%s' ]", function_name, function, realpath); + function(); + TRACE("[ Done calling d-tor %s @ %p for '%s' ]", function_name, function, realpath); +} + +template +static void call_array(const char* array_name __unused, + F* functions, + size_t count, + bool reverse, + const char* realpath) { if (functions == nullptr) { return; } - TRACE("[ Calling %s (size %zd) @ %p for '%s' ]", array_name, count, functions, get_realpath()); + TRACE("[ Calling %s (size %zd) @ %p for '%s' ]", array_name, count, functions, realpath); int begin = reverse ? (count - 1) : 0; int end = reverse ? -1 : count; @@ -3034,26 +3066,16 @@ void soinfo::call_array(const char* array_name __unused, linker_function_t* func for (int i = begin; i != end; i += step) { TRACE("[ %s[%d] == %p ]", array_name, i, functions[i]); - call_function("function", functions[i]); - } - - TRACE("[ Done calling %s for '%s' ]", array_name, get_realpath()); -} - -void soinfo::call_function(const char* function_name __unused, linker_function_t function) { - if (function == nullptr || reinterpret_cast(function) == static_cast(-1)) { - return; + call_function("function", functions[i], realpath); } - TRACE("[ Calling %s @ %p for '%s' ]", function_name, function, get_realpath()); - function(); - TRACE("[ Done calling %s @ %p for '%s' ]", function_name, function, get_realpath()); + TRACE("[ Done calling %s for '%s' ]", array_name, realpath); } void soinfo::call_pre_init_constructors() { // DT_PREINIT_ARRAY functions are called before any other constructors for executables, // but ignored in a shared library. - call_array("DT_PREINIT_ARRAY", preinit_array_, preinit_array_count_, false); + call_array("DT_PREINIT_ARRAY", preinit_array_, preinit_array_count_, false, get_realpath()); } void soinfo::call_constructors() { @@ -3085,8 +3107,8 @@ void soinfo::call_constructors() { TRACE("\"%s\": calling constructors", get_realpath()); // DT_INIT should be called before DT_INIT_ARRAY if both are present. - call_function("DT_INIT", init_func_); - call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false); + call_function("DT_INIT", init_func_, get_realpath()); + call_array("DT_INIT_ARRAY", init_array_, init_array_count_, false, get_realpath()); } void soinfo::call_destructors() { @@ -3096,10 +3118,10 @@ void soinfo::call_destructors() { TRACE("\"%s\": calling destructors", get_realpath()); // DT_FINI_ARRAY must be parsed in reverse order. - call_array("DT_FINI_ARRAY", fini_array_, fini_array_count_, true); + call_array("DT_FINI_ARRAY", fini_array_, fini_array_count_, true, get_realpath()); // DT_FINI should be called after DT_FINI_ARRAY if both are present. - call_function("DT_FINI", fini_func_); + call_function("DT_FINI", fini_func_, get_realpath()); } void soinfo::add_child(soinfo* child) { @@ -3641,17 +3663,17 @@ bool soinfo::prelink_image() { #endif case DT_INIT: - init_func_ = reinterpret_cast(load_bias + d->d_un.d_ptr); + init_func_ = reinterpret_cast(load_bias + d->d_un.d_ptr); DEBUG("%s constructors (DT_INIT) found at %p", get_realpath(), init_func_); break; case DT_FINI: - fini_func_ = reinterpret_cast(load_bias + d->d_un.d_ptr); + fini_func_ = reinterpret_cast(load_bias + d->d_un.d_ptr); DEBUG("%s destructors (DT_FINI) found at %p", get_realpath(), fini_func_); break; case DT_INIT_ARRAY: - init_array_ = reinterpret_cast(load_bias + d->d_un.d_ptr); + init_array_ = reinterpret_cast(load_bias + d->d_un.d_ptr); DEBUG("%s constructors (DT_INIT_ARRAY) found at %p", get_realpath(), init_array_); break; @@ -3660,7 +3682,7 @@ bool soinfo::prelink_image() { break; case DT_FINI_ARRAY: - fini_array_ = reinterpret_cast(load_bias + d->d_un.d_ptr); + fini_array_ = reinterpret_cast(load_bias + d->d_un.d_ptr); DEBUG("%s destructors (DT_FINI_ARRAY) found at %p", get_realpath(), fini_array_); break; @@ -3669,7 +3691,7 @@ bool soinfo::prelink_image() { break; case DT_PREINIT_ARRAY: - preinit_array_ = reinterpret_cast(load_bias + d->d_un.d_ptr); + preinit_array_ = reinterpret_cast(load_bias + d->d_un.d_ptr); DEBUG("%s constructors (DT_PREINIT_ARRAY) found at %p", get_realpath(), preinit_array_); break; @@ -4188,7 +4210,7 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( ElfW(Ehdr)* elf_hdr = reinterpret_cast(si->base); if (elf_hdr->e_type != ET_DYN) { __libc_fatal("\"%s\": error: only position independent executables (PIE) are supported.", - g_argv0); + g_argv[0]); } // Use LD_LIBRARY_PATH and LD_PRELOAD (but only if we aren't setuid/setgid). @@ -4200,7 +4222,7 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( init_default_namespace(); if (!si->prelink_image()) { - __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv0, linker_get_error_buffer()); + __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer()); } // add somain to global group @@ -4231,10 +4253,10 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( !find_libraries(&g_default_namespace, si, needed_library_names, needed_libraries_count, nullptr, &g_ld_preloads, ld_preloads_count, RTLD_GLOBAL, nullptr, /* add_as_children */ true)) { - __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv0, linker_get_error_buffer()); + __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer()); } else if (needed_libraries_count == 0) { if (!si->link_image(g_empty_list, soinfo::soinfo_list_t::make_list(si), nullptr)) { - __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv0, linker_get_error_buffer()); + __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer()); } si->increment_ref_count(); } @@ -4257,12 +4279,12 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( #if TIMING gettimeofday(&t1, nullptr); - PRINT("LINKER TIME: %s: %d microseconds", g_argv0, (int) ( + PRINT("LINKER TIME: %s: %d microseconds", g_argv[0], (int) ( (((long long)t1.tv_sec * 1000000LL) + (long long)t1.tv_usec) - (((long long)t0.tv_sec * 1000000LL) + (long long)t0.tv_usec))); #endif #if STATS - PRINT("RELO STATS: %s: %d abs, %d rel, %d copy, %d symbol", g_argv0, + PRINT("RELO STATS: %s: %d abs, %d rel, %d copy, %d symbol", g_argv[0], linker_stats.count[kRelocAbsolute], linker_stats.count[kRelocRelative], linker_stats.count[kRelocCopy], @@ -4288,7 +4310,7 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args, ElfW( } } } - PRINT("PAGES MODIFIED: %s: %d (%dKB)", g_argv0, count, count * 4); + PRINT("PAGES MODIFIED: %s: %d (%dKB)", g_argv[0], count, count * 4); } #endif @@ -4326,7 +4348,7 @@ static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf) { } static void __linker_cannot_link() { - __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv0, linker_get_error_buffer()); + __libc_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer()); } /* @@ -4341,7 +4363,9 @@ static void __linker_cannot_link() { extern "C" ElfW(Addr) __linker_init(void* raw_args) { KernelArgumentBlock args(raw_args); - g_argv0 = args.argv[0]; + g_argc = args.argc; + g_argv = args.argv; + g_envp = args.envp; ElfW(Addr) linker_addr = args.getauxval(AT_BASE); ElfW(Addr) entry_point = args.getauxval(AT_ENTRY); @@ -4359,7 +4383,7 @@ extern "C" ElfW(Addr) __linker_init(void* raw_args) { if (reinterpret_cast(&_start) == entry_point) { __libc_format_fd(STDOUT_FILENO, "This is %s, the helper program for shared library executables.\n", - g_argv0); + g_argv[0]); exit(0); } diff --git a/linker/linker.h b/linker/linker.h index fd437aace..d8c0e1970 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -96,7 +96,8 @@ #define SOINFO_NAME_LEN 128 #endif -typedef void (*linker_function_t)(); +typedef void (*linker_dtor_function_t)(); +typedef void (*linker_ctor_function_t)(int, char**, char**); // Android uses RELA for aarch64 and x86_64. mips64 still uses REL. #if defined(__aarch64__) || defined(__x86_64__) @@ -234,16 +235,16 @@ struct soinfo { size_t rel_count_; #endif - linker_function_t* preinit_array_; + linker_ctor_function_t* preinit_array_; size_t preinit_array_count_; - linker_function_t* init_array_; + linker_ctor_function_t* init_array_; size_t init_array_count_; - linker_function_t* fini_array_; + linker_dtor_function_t* fini_array_; size_t fini_array_count_; - linker_function_t init_func_; - linker_function_t fini_func_; + linker_ctor_function_t init_func_; + linker_dtor_function_t fini_func_; #if defined(__arm__) public: @@ -373,8 +374,6 @@ struct soinfo { bool lookup_version_info(const VersionTracker& version_tracker, ElfW(Word) sym, const char* sym_name, const version_info** vi); - void call_array(const char* array_name, linker_function_t* functions, size_t count, bool reverse); - void call_function(const char* function_name, linker_function_t function); template bool relocate(const VersionTracker& version_tracker, ElfRelIteratorT&& rel_iterator, const soinfo_list_t& global_group, const soinfo_list_t& local_group); diff --git a/linker/linker_logger.cpp b/linker/linker_logger.cpp index 78f14d477..a9d358a1e 100644 --- a/linker/linker_logger.cpp +++ b/linker/linker_logger.cpp @@ -87,7 +87,7 @@ void LinkerLogger::ResetState() { flags_ |= ParseProperty(value); // get process basename - std::string process_name = basename(g_argv0); + std::string process_name = basename(g_argv[0]); std::string property_name = std::string(kLdDebugPropertyPrefix) + process_name; diff --git a/linker/linker_logger.h b/linker/linker_logger.h index 6b06affc0..093247107 100644 --- a/linker/linker_logger.h +++ b/linker/linker_logger.h @@ -54,6 +54,6 @@ class LinkerLogger { }; extern LinkerLogger g_linker_logger; -extern const char* g_argv0; +extern char** g_argv; #endif /* _LINKER_LOGGER_H_ */ diff --git a/tests/dlfcn_test.cpp b/tests/dlfcn_test.cpp index 56df1a660..5bf586197 100644 --- a/tests/dlfcn_test.cpp +++ b/tests/dlfcn_test.cpp @@ -38,15 +38,24 @@ extern "C" void DlSymTestFunction() { } static int g_ctor_function_called = 0; +static int g_ctor_argc = 0; +static char** g_ctor_argv = reinterpret_cast(0xDEADBEEF); +static char** g_ctor_envp = g_ctor_envp; -extern "C" void ctor_function() __attribute__ ((constructor)); +extern "C" void ctor_function(int argc, char** argv, char** envp) __attribute__ ((constructor)); -extern "C" void ctor_function() { +extern "C" void ctor_function(int argc, char** argv, char** envp) { g_ctor_function_called = 17; + g_ctor_argc = argc; + g_ctor_argv = argv; + g_ctor_envp = envp; } TEST(dlfcn, ctor_function_call) { ASSERT_EQ(17, g_ctor_function_called); + ASSERT_TRUE(g_ctor_argc = get_argc()); + ASSERT_TRUE(g_ctor_argv = get_argv()); + ASSERT_TRUE(g_ctor_envp = get_envp()); } TEST(dlfcn, dlsym_in_executable) { diff --git a/tests/gtest_main.cpp b/tests/gtest_main.cpp index 2b5864688..a08a2e154 100644 --- a/tests/gtest_main.cpp +++ b/tests/gtest_main.cpp @@ -47,11 +47,26 @@ #endif static std::string g_executable_path; +static int g_argc; +static char** g_argv; +static char** g_envp; const std::string& get_executable_path() { return g_executable_path; } +int get_argc() { + return g_argc; +} + +char** get_argv() { + return g_argv; +} + +char** get_envp() { + return g_envp; +} + namespace testing { namespace internal { @@ -1122,8 +1137,11 @@ static std::string get_proc_self_exe() { return std::string(path, path_len); } -int main(int argc, char** argv) { +int main(int argc, char** argv, char** envp) { g_executable_path = get_proc_self_exe(); + g_argc = argc; + g_argv = argv; + g_envp = envp; 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 f8e003922..08e8cea52 100644 --- a/tests/utils.h +++ b/tests/utils.h @@ -122,4 +122,9 @@ static inline void AssertChildExited(int pid, int expected_exit_status) { // The absolute path to the executable const std::string& get_executable_path(); +// Access to argc/argv/envp +int get_argc(); +char** get_argv(); +char** get_envp(); + #endif -- 2.11.0