diff options
Diffstat (limited to 'linker/linker.cpp')
-rw-r--r-- | linker/linker.cpp | 123 |
1 files changed, 96 insertions, 27 deletions
diff --git a/linker/linker.cpp b/linker/linker.cpp index df7dd4020..0bc312129 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -46,8 +46,8 @@ #include <android-base/properties.h> #include <android-base/scopeguard.h> - #include <async_safe/log.h> +#include <bionic/pthread_internal.h> // Private C library headers. @@ -76,6 +76,7 @@ static std::unordered_map<void*, size_t> g_dso_handle_counters; +static bool g_anonymous_namespace_set = false; static android_namespace_t* g_anonymous_namespace = &g_default_namespace; static std::unordered_map<std::string, android_namespace_t*> g_exported_namespaces; @@ -85,11 +86,16 @@ static LinkerTypeAllocator<LinkedListEntry<soinfo>> g_soinfo_links_allocator; static LinkerTypeAllocator<android_namespace_t> g_namespace_allocator; static LinkerTypeAllocator<LinkedListEntry<android_namespace_t>> g_namespace_list_allocator; +static uint64_t g_module_load_counter = 0; +static uint64_t g_module_unload_counter = 0; + static const char* const kLdConfigArchFilePath = "/system/etc/ld.config." ABI_STRING ".txt"; static const char* const kLdConfigFilePath = "/system/etc/ld.config.txt"; static const char* const kLdConfigVndkLiteFilePath = "/system/etc/ld.config.vndk_lite.txt"; +static const char* const kLdGeneratedConfigFilePath = "/dev/linkerconfig/ld.config.txt"; + #if defined(__LP64__) static const char* const kSystemLibDir = "/system/lib64"; static const char* const kOdmLibDir = "/odm/lib64"; @@ -97,7 +103,7 @@ static const char* const kVendorLibDir = "/vendor/lib64"; static const char* const kAsanSystemLibDir = "/data/asan/system/lib64"; static const char* const kAsanOdmLibDir = "/data/asan/odm/lib64"; static const char* const kAsanVendorLibDir = "/data/asan/vendor/lib64"; -static const char* const kRuntimeApexLibDir = "/apex/com.android.runtime/lib64"; +static const char* const kArtApexLibDir = "/apex/com.android.art/lib64"; #else static const char* const kSystemLibDir = "/system/lib"; static const char* const kOdmLibDir = "/odm/lib"; @@ -105,7 +111,7 @@ static const char* const kVendorLibDir = "/vendor/lib"; static const char* const kAsanSystemLibDir = "/data/asan/system/lib"; static const char* const kAsanOdmLibDir = "/data/asan/odm/lib"; static const char* const kAsanVendorLibDir = "/data/asan/vendor/lib"; -static const char* const kRuntimeApexLibDir = "/apex/com.android.runtime/lib"; +static const char* const kArtApexLibDir = "/apex/com.android.art/lib"; #endif static const char* const kAsanLibDirPrefix = "/data/asan"; @@ -239,7 +245,7 @@ static bool is_greylisted(android_namespace_t* ns, const char* name, const soinf * return true if translation is needed */ static bool translateSystemPathToApexPath(const char* name, std::string* out_name_to_apex) { - static const char* const kSystemToRuntimeApexLibs[] = { + static const char* const kSystemToArtApexLibs[] = { "libicuuc.so", "libicui18n.so", }; @@ -257,9 +263,9 @@ static bool translateSystemPathToApexPath(const char* name, std::string* out_nam const char* base_name = basename(name); - for (const char* soname : kSystemToRuntimeApexLibs) { + for (const char* soname : kSystemToArtApexLibs) { if (strcmp(base_name, soname) == 0) { - *out_name_to_apex = std::string(kRuntimeApexLibDir) + "/" + base_name; + *out_name_to_apex = std::string(kArtApexLibDir) + "/" + base_name; return true; } } @@ -270,8 +276,6 @@ static bool translateSystemPathToApexPath(const char* name, std::string* out_nam static std::vector<std::string> g_ld_preload_names; -static bool g_anonymous_namespace_initialized; - #if STATS struct linker_stats_t { int count[kRelocMax]; @@ -423,6 +427,24 @@ static bool realpath_fd(int fd, std::string* realpath) { return true; } +// Returns the address of the current thread's copy of a TLS module. If the current thread doesn't +// have a copy yet, allocate one on-demand if should_alloc is true, and return nullptr otherwise. +static inline void* get_tls_block_for_this_thread(const soinfo_tls* si_tls, bool should_alloc) { + const TlsModule& tls_mod = get_tls_module(si_tls->module_id); + if (tls_mod.static_offset != SIZE_MAX) { + const StaticTlsLayout& layout = __libc_shared_globals()->static_tls_layout; + char* static_tls = reinterpret_cast<char*>(__get_bionic_tcb()) - layout.offset_bionic_tcb(); + return static_tls + tls_mod.static_offset; + } else if (should_alloc) { + const TlsIndex ti { si_tls->module_id, 0 }; + return TLS_GET_ADDR(&ti); + } else { + TlsDtv* dtv = __get_tcb_dtv(__get_bionic_tcb()); + if (dtv->generation < tls_mod.first_generation) return nullptr; + return dtv->modules[__tls_module_id_to_idx(si_tls->module_id)]; + } +} + #if defined(__arm__) // For a given PC, find the .so that it belongs to. @@ -452,6 +474,16 @@ int do_dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), v dl_info.dlpi_name = si->link_map_head.l_name; dl_info.dlpi_phdr = si->phdr; dl_info.dlpi_phnum = si->phnum; + dl_info.dlpi_adds = g_module_load_counter; + dl_info.dlpi_subs = g_module_unload_counter; + if (soinfo_tls* tls_module = si->get_tls()) { + dl_info.dlpi_tls_modid = tls_module->module_id; + dl_info.dlpi_tls_data = get_tls_block_for_this_thread(tls_module, /*should_alloc=*/false); + } else { + dl_info.dlpi_tls_modid = 0; + dl_info.dlpi_tls_data = nullptr; + } + rv = cb(&dl_info, sizeof(dl_phdr_info), data); if (rv != 0) { break; @@ -951,7 +983,9 @@ static const ElfW(Sym)* dlsym_linear_lookup(android_namespace_t* ns, } soinfo* find_containing_library(const void* p) { - ElfW(Addr) address = reinterpret_cast<ElfW(Addr)>(p); + // Addresses within a library may be tagged if they point to globals. Untag + // them so that the bounds check succeeds. + ElfW(Addr) address = reinterpret_cast<ElfW(Addr)>(untag_address(p)); for (soinfo* si = solist_get_head(); si != nullptr; si = si->next) { if (address < si->base || address - si->base >= si->size) { continue; @@ -1901,6 +1935,9 @@ bool find_libraries(android_namespace_t* ns, // flag is set. link_extinfo = extinfo; } + if (__libc_shared_globals()->load_hook) { + __libc_shared_globals()->load_hook(si->load_bias, si->phdr, si->phnum); + } if (!si->link_image(global_group, local_group, link_extinfo, &relro_fd_offset) || !get_cfi_shadow()->AfterLoad(si, solist_get_head())) { return false; @@ -2037,8 +2074,12 @@ static void soinfo_unload_impl(soinfo* root) { "... dlclose: unloading \"%s\"@%p ...", si->get_realpath(), si); + ++g_module_unload_counter; notify_gdb_of_unload(si); unregister_soinfo_tls(si); + if (__libc_shared_globals()->unload_hook) { + __libc_shared_globals()->unload_hook(si->load_bias, si->phdr, si->phnum); + } get_cfi_shadow()->BeforeUnload(si); soinfo_free(si); } @@ -2420,18 +2461,15 @@ bool do_dlsym(void* handle, if ((bind == STB_GLOBAL || bind == STB_WEAK) && sym->st_shndx != 0) { if (type == STT_TLS) { // For a TLS symbol, dlsym returns the address of the current thread's - // copy of the symbol. This function may allocate a DTV and/or storage - // for the source TLS module. (Allocating a DTV isn't necessary if the - // symbol is part of static TLS, but it's simpler to reuse - // __tls_get_addr.) - soinfo_tls* tls_module = found->get_tls(); + // copy of the symbol. + const soinfo_tls* tls_module = found->get_tls(); if (tls_module == nullptr) { DL_ERR("TLS symbol \"%s\" in solib \"%s\" with no TLS segment", sym_name, found->get_realpath()); return false; } - const TlsIndex ti { tls_module->module_id, sym->st_value }; - *symbol = TLS_GET_ADDR(&ti); + void* tls_block = get_tls_block_for_this_thread(tls_module, /*should_alloc=*/true); + *symbol = static_cast<char*>(tls_block) + sym->st_value; } else { *symbol = reinterpret_cast<void*>(found->resolve_symbol_address(sym)); } @@ -2471,14 +2509,29 @@ int do_dlclose(void* handle) { return 0; } -bool init_anonymous_namespace(const char* shared_lib_sonames, const char* library_search_path) { - if (g_anonymous_namespace_initialized) { - DL_ERR("anonymous namespace has already been initialized."); - return false; +// Make ns as the anonymous namespace that is a namespace used when +// we fail to determine the caller address (e.g., call from mono-jited code) +// Since there can be multiple anonymous namespace in a process, subsequent +// call to this function causes an error. +static bool set_anonymous_namespace(android_namespace_t* ns) { + if (!g_anonymous_namespace_set && ns != nullptr) { + CHECK(ns->is_also_used_as_anonymous()); + g_anonymous_namespace = ns; + g_anonymous_namespace_set = true; + return true; } + return false; +} +// TODO(b/130388701) remove this. Currently, this is used only for testing +// where we don't have classloader namespace. +bool init_anonymous_namespace(const char* shared_lib_sonames, const char* library_search_path) { ProtectedDataGuard guard; + // Test-only feature: we need to change the anonymous namespace multiple times + // while the test is running. + g_anonymous_namespace_set = false; + // create anonymous namespace // When the caller is nullptr - create_namespace will take global group // from the anonymous namespace, which is fine because anonymous namespace @@ -2488,21 +2541,18 @@ bool init_anonymous_namespace(const char* shared_lib_sonames, const char* librar "(anonymous)", nullptr, library_search_path, - ANDROID_NAMESPACE_TYPE_ISOLATED, + ANDROID_NAMESPACE_TYPE_ISOLATED | + ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS, nullptr, &g_default_namespace); - if (anon_ns == nullptr) { - return false; - } + CHECK(anon_ns != nullptr); if (!link_namespaces(anon_ns, &g_default_namespace, shared_lib_sonames)) { + // TODO: delete anon_ns return false; } - g_anonymous_namespace = anon_ns; - g_anonymous_namespace_initialized = true; - return true; } @@ -2542,6 +2592,7 @@ android_namespace_t* create_namespace(const void* caller_addr, ns->set_name(name); ns->set_isolated((type & ANDROID_NAMESPACE_TYPE_ISOLATED) != 0); ns->set_greylist_enabled((type & ANDROID_NAMESPACE_TYPE_GREYLIST_ENABLED) != 0); + ns->set_also_used_as_anonymous((type & ANDROID_NAMESPACE_TYPE_ALSO_USED_AS_ANONYMOUS) != 0); if ((type & ANDROID_NAMESPACE_TYPE_SHARED) != 0) { // append parent namespace paths. @@ -2573,6 +2624,16 @@ android_namespace_t* create_namespace(const void* caller_addr, ns->set_default_library_paths(std::move(default_library_paths)); ns->set_permitted_paths(std::move(permitted_paths)); + if (ns->is_also_used_as_anonymous() && !set_anonymous_namespace(ns)) { + DL_ERR("failed to set namespace: [name=\"%s\", ld_library_path=\"%s\", default_library_paths=\"%s\"" + " permitted_paths=\"%s\"] as the anonymous namespace", + ns->get_name(), + android::base::Join(ns->get_ld_library_paths(), ':').c_str(), + android::base::Join(ns->get_default_library_paths(), ':').c_str(), + android::base::Join(ns->get_permitted_paths(), ':').c_str()); + return nullptr; + } + return ns; } @@ -4074,6 +4135,7 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& } } + ++g_module_load_counter; notify_gdb_of_load(this); set_image_linked(); return true; @@ -4142,6 +4204,13 @@ static std::string get_ld_config_file_path(const char* executable_path) { } #endif + // Use generated linker config if flag is set + // TODO(b/138920271) Do not check property once it is confirmed as stable + if (android::base::GetBoolProperty("sys.linker.use_generated_config", false) && + file_exists(kLdGeneratedConfigFilePath)) { + return kLdGeneratedConfigFilePath; + } + std::string path = get_ld_config_file_apex_path(executable_path); if (!path.empty()) { if (file_exists(path.c_str())) { |