diff options
author | Jiyong Park <jiyong@google.com> | 2017-05-20 01:01:24 +0900 |
---|---|---|
committer | Jiyong Park <jiyong@google.com> | 2017-08-03 01:02:07 +0900 |
commit | 02586a2a34e6acfccf359b94db840f422b6c0231 (patch) | |
tree | 9dde1a2bcf8a004b23fe41b9f3e497895d44e44d /linker/linker.cpp | |
parent | 5ac5a7c0839a123c2982b37b383a7691fd216e7e (diff) |
linker: the global group is added to all built-in namespaces
With ld.config.txt, we now have multiple built-in namespaces other than
the default namespace. Libs (and their dependents) listed in LD_PRELOAD
must be visible to those additional namespaces as well.
This also adds a debugging only feature: path to the linker config file
can be customized via LD_CONFIG_FILE environment variable. This works
only for debuggable builds.
Bug: 38114603
Bug: 62815515
Test: 1. ./external/compiler-rt/lib/asan/scripts/asan_device_setup --lib
prebuilts/clang/host/linux-x86/clang-stable/lib64/clang/5.0/lib/linux
2. enable talkback shortcut
3. in the home screen, hold vol-up/down together
4. device does not reboots and talkback shortcut is toggled
Test: bionic-unit-tests and linker-unit-tests successful
Change-Id: I9a03591053f4a9caea82f0dcb23e7a3d324bb9bd
Diffstat (limited to 'linker/linker.cpp')
-rw-r--r-- | linker/linker.cpp | 218 |
1 files changed, 127 insertions, 91 deletions
diff --git a/linker/linker.cpp b/linker/linker.cpp index 8e7a1411c..a21262417 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -558,9 +558,10 @@ class LoadTask { static LoadTask* create(const char* name, soinfo* needed_by, + android_namespace_t* start_from, std::unordered_map<const soinfo*, ElfReader>* readers_map) { LoadTask* ptr = TypeBasedAllocator<LoadTask>::alloc(); - return new (ptr) LoadTask(name, needed_by, readers_map); + return new (ptr) LoadTask(name, needed_by, start_from, readers_map); } const char* get_name() const { @@ -612,6 +613,11 @@ class LoadTask { is_dt_needed_ = is_dt_needed; } + // returns the namespace from where we need to start loading this. + const android_namespace_t* get_start_from() const { + return start_from_; + } + const ElfReader& get_elf_reader() const { CHECK(si_ != nullptr); return (*elf_readers_map_)[si_]; @@ -650,10 +656,11 @@ class LoadTask { private: LoadTask(const char* name, soinfo* needed_by, + android_namespace_t* start_from, std::unordered_map<const soinfo*, ElfReader>* readers_map) : name_(name), needed_by_(needed_by), si_(nullptr), fd_(-1), close_fd_(false), file_offset_(0), elf_readers_map_(readers_map), - is_dt_needed_(false) {} + is_dt_needed_(false), start_from_(start_from) {} ~LoadTask() { if (fd_ != -1 && close_fd_) { @@ -672,6 +679,7 @@ class LoadTask { // TODO(dimitry): needed by workaround for http://b/26394120 (the grey-list) bool is_dt_needed_; // END OF WORKAROUND + const android_namespace_t* const start_from_; DISALLOW_IMPLICIT_CONSTRUCTORS(LoadTask); }; @@ -1041,7 +1049,7 @@ static int open_library(android_namespace_t* ns, ZipArchiveCache* zip_archive_cache, const char* name, soinfo *needed_by, off64_t* file_offset, std::string* realpath) { - TRACE("[ opening %s ]", name); + TRACE("[ opening %s at namespace %s]", name, ns->get_name()); // If the name contains a slash, we should attempt to open it directly and not search the paths. if (strchr(name, '/') != nullptr) { @@ -1273,7 +1281,7 @@ static bool load_library(android_namespace_t* ns, } for_each_dt_needed(task->get_elf_reader(), [&](const char* name) { - load_tasks->push_back(LoadTask::create(name, si, task->get_readers_map())); + load_tasks->push_back(LoadTask::create(name, si, ns, task->get_readers_map())); }); return true; @@ -1368,8 +1376,7 @@ static bool find_loaded_library_by_soname(android_namespace_t* ns, } static bool find_library_in_linked_namespace(const android_namespace_link_t& namespace_link, - LoadTask* task, - int rtld_flags) { + LoadTask* task) { android_namespace_t* ns = namespace_link.linked_namespace(); soinfo* candidate; @@ -1394,29 +1401,10 @@ static bool find_library_in_linked_namespace(const android_namespace_link_t& nam return true; } - // try to load the library - once namespace boundary is crossed - // we need to load a library within separate load_group - // to avoid using symbols from foreign namespace while. - // - // All symbols during relocation should be resolved within a - // namespace to preserve library locality to a namespace. - const char* name = task->get_name(); - if (find_libraries(ns, - task->get_needed_by(), - &name, - 1, - &candidate, - nullptr /* ld_preloads */, - 0 /* ld_preload_count*/, - rtld_flags, - nullptr /* extinfo*/, - false /* add_as_children */, - false /* search_linked_namespaces */)) { - task->set_soinfo(candidate); - return true; - } - - return false; + // returning true with empty soinfo means that the library is okay to be + // loaded in the namespace buy has not yet been loaded there before. + task->set_soinfo(nullptr); + return true; } static bool find_library_internal(android_namespace_t* ns, @@ -1445,9 +1433,24 @@ static bool find_library_internal(android_namespace_t* ns, // if a library was not found - look into linked namespaces for (auto& linked_namespace : ns->linked_namespaces()) { if (find_library_in_linked_namespace(linked_namespace, - task, - rtld_flags)) { - return true; + task)) { + if (task->get_soinfo() == nullptr) { + // try to load the library - once namespace boundary is crossed + // we need to load a library within separate load_group + // to avoid using symbols from foreign namespace while. + // + // However, actual linking is deferred until when the global group + // is fully identified and is applied to all namespaces. + // Otherwise, the libs in the linked namespace won't get symbols from + // the global group. + if (load_library(linked_namespace.linked_namespace(), task, zip_archive_cache, load_tasks, rtld_flags, false)) { + return true; + } + // lib was not found in the namespace. Try next linked namespace. + } else { + // lib is already loaded + return true; + } } } } @@ -1458,44 +1461,6 @@ static bool find_library_internal(android_namespace_t* ns, static void soinfo_unload(soinfo* si); static void soinfo_unload(soinfo* soinfos[], size_t count); -// TODO: this is slightly unusual way to construct -// the global group for relocation. Not every RTLD_GLOBAL -// library is included in this group for backwards-compatibility -// reasons. -// -// This group consists of the main executable, LD_PRELOADs -// and libraries with the DF_1_GLOBAL flag set. -static soinfo_list_t make_global_group(android_namespace_t* ns) { - soinfo_list_t global_group; - ns->soinfo_list().for_each([&](soinfo* si) { - if ((si->get_dt_flags_1() & DF_1_GLOBAL) != 0) { - global_group.push_back(si); - } - }); - - return global_group; -} - -// This function provides a list of libraries to be shared -// by the namespace. For the default namespace this is the global -// group (see make_global_group). For all others this is a group -// of RTLD_GLOBAL libraries (which includes the global group from -// the default namespace). -static soinfo_list_t get_shared_group(android_namespace_t* ns) { - if (ns == &g_default_namespace) { - return make_global_group(ns); - } - - soinfo_list_t shared_group; - ns->soinfo_list().for_each([&](soinfo* si) { - if ((si->get_rtld_flags() & RTLD_GLOBAL) != 0) { - shared_group.push_back(si); - } - }); - - return shared_group; -} - static void shuffle(std::vector<LoadTask*>* v) { for (size_t i = 0, size = v->size(); i < size; ++i) { size_t n = size - i; @@ -1518,19 +1483,17 @@ bool find_libraries(android_namespace_t* ns, int rtld_flags, const android_dlextinfo* extinfo, bool add_as_children, - bool search_linked_namespaces) { + bool search_linked_namespaces, + std::unordered_map<const soinfo*, ElfReader>& readers_map, + std::vector<android_namespace_t*>* namespaces) { // Step 0: prepare. LoadTaskList load_tasks; - std::unordered_map<const soinfo*, ElfReader> readers_map; for (size_t i = 0; i < library_names_count; ++i) { const char* name = library_names[i]; - load_tasks.push_back(LoadTask::create(name, start_with, &readers_map)); + load_tasks.push_back(LoadTask::create(name, start_with, ns, &readers_map)); } - // Construct global_group. - soinfo_list_t global_group = make_global_group(ns); - // If soinfos array is null allocate one on stack. // The array is needed in case of failure; for example // when library_names[] = {libone.so, libtwo.so} and libone.so @@ -1570,7 +1533,12 @@ bool find_libraries(android_namespace_t* ns, task->set_extinfo(is_dt_needed ? nullptr : extinfo); task->set_dt_needed(is_dt_needed); - if (!find_library_internal(ns, + // try to find the load. + // Note: start from the namespace that is stored in the LoadTask. This namespace + // is different from the current namespace when the LoadTask is for a transitive + // dependency and the lib that created the LoadTask is not found in the + // current namespace but in one of the linked namespace. + if (!find_library_internal(const_cast<android_namespace_t*>(task->get_start_from()), task, &zip_archive_cache, &load_tasks, @@ -1629,18 +1597,61 @@ bool find_libraries(android_namespace_t* ns, } } - // Step 4: Add LD_PRELOADed libraries to the global group for - // future runs. There is no need to explicitly add them to - // the global group for this run because they are going to - // appear in the local group in the correct order. + // Step 4: Construct the global group. Note: DF_1_GLOBAL bit of a library is + // determined at step 3. + + // Step 4-1: DF_1_GLOBAL bit is force set for LD_PRELOADed libs because they + // must be added to the global group if (ld_preloads != nullptr) { for (auto&& si : *ld_preloads) { si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL); } } + // Step 4-2: Gather all DF_1_GLOBAL libs which were newly loaded during this + // run. These will be the new member of the global group + soinfo_list_t new_global_group_members; + for (auto&& task : load_tasks) { + soinfo* si = task->get_soinfo(); + if (!si->is_linked() && (si->get_dt_flags_1() & DF_1_GLOBAL) != 0) { + new_global_group_members.push_back(si); + } + } + + // Step 4-3: Add the new global group members to all the linked namespaces + for (auto si : new_global_group_members) { + for (auto linked_ns : *namespaces) { + if (si->get_primary_namespace() != linked_ns) { + linked_ns->add_soinfo(si); + si->add_secondary_namespace(linked_ns); + } + } + } - // Step 5: link libraries. + // Step 5: link libraries that are not destined to this namespace. + // Do this by recursively calling find_libraries on the namespace where the lib + // was found during Step 1. + for (auto&& task : load_tasks) { + soinfo* si = task->get_soinfo(); + if (si->get_primary_namespace() != ns) { + const char* name = task->get_name(); + if (find_libraries(si->get_primary_namespace(), task->get_needed_by(), &name, 1, + nullptr /* soinfos */, nullptr /* ld_preloads */, 0 /* ld_preload_count */, + rtld_flags, nullptr /* extinfo */, false /* add_as_children */, + false /* search_linked_namespaces */, readers_map, namespaces)) { + // If this lib is directly needed by one of the libs in this namespace, + // then increment the count + soinfo* needed_by = task->get_needed_by(); + if (needed_by != nullptr && needed_by->get_primary_namespace() == ns && si->is_linked()) { + si->increment_ref_count(); + } + } else { + return false; + } + } + } + + // Step 6: link libraries in this namespace soinfo_list_t local_group; walk_dependencies_tree( (start_with != nullptr && add_as_children) ? &start_with : soinfos, @@ -1654,6 +1665,7 @@ bool find_libraries(android_namespace_t* ns, } }); + soinfo_list_t global_group = ns->get_global_group(); bool linked = local_group.visit([&](soinfo* si) { if (!si->is_linked()) { if (!si->link_image(global_group, local_group, extinfo) || @@ -1684,6 +1696,9 @@ static soinfo* find_library(android_namespace_t* ns, soinfo* needed_by) { soinfo* si; + // readers_map is shared across recursive calls to find_libraries. + // However, the map is not shared across different threads. + std::unordered_map<const soinfo*, ElfReader> readers_map; if (name == nullptr) { si = solist_get_somain(); } else if (!find_libraries(ns, @@ -1696,7 +1711,8 @@ static soinfo* find_library(android_namespace_t* ns, rtld_flags, extinfo, false /* add_as_children */, - true /* search_linked_namespaces */)) { + true /* search_linked_namespaces */, + readers_map)) { return nullptr; } @@ -2208,7 +2224,7 @@ android_namespace_t* create_namespace(const void* caller_addr, } } else { // If not shared - copy only the shared group - add_soinfos_to_namespace(get_shared_group(parent_namespace), ns); + add_soinfos_to_namespace(parent_namespace->get_shared_group(), ns); } ns->set_ld_library_paths(std::move(ld_library_paths)); @@ -3413,7 +3429,7 @@ bool soinfo::protect_relro() { return true; } -static void init_default_namespace_no_config(bool is_asan) { +static std::vector<android_namespace_t*> init_default_namespace_no_config(bool is_asan) { g_default_namespace.set_isolated(false); auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : kDefaultLdPaths; @@ -3428,9 +3444,13 @@ static void init_default_namespace_no_config(bool is_asan) { } g_default_namespace.set_default_library_paths(std::move(ld_default_paths)); + + std::vector<android_namespace_t*> namespaces; + namespaces.push_back(&g_default_namespace); + return namespaces; } -void init_default_namespace(const char* executable_path) { +std::vector<android_namespace_t*> init_default_namespaces(const char* executable_path) { g_default_namespace.set_name("(default)"); soinfo* somain = solist_get_somain(); @@ -3447,14 +3467,24 @@ void init_default_namespace(const char* executable_path) { std::string error_msg; - if (!Config::read_binary_config(kLdConfigFilePath, + const char* config_file = kLdConfigFilePath; +#ifdef USE_LD_CONFIG_FILE + // This is a debugging/testing only feature. Must not be available on + // production builds. + const char* ld_config_file = getenv("LD_CONFIG_FILE"); + if (ld_config_file != nullptr && file_exists(ld_config_file)) { + config_file = ld_config_file; + } +#endif + + if (!Config::read_binary_config(config_file, executable_path, g_is_asan, &config, &error_msg)) { if (!error_msg.empty()) { DL_WARN("error reading config file \"%s\" for \"%s\" (will use default configuration): %s", - kLdConfigFilePath, + config_file, executable_path, error_msg.c_str()); } @@ -3462,8 +3492,7 @@ void init_default_namespace(const char* executable_path) { } if (config == nullptr) { - init_default_namespace_no_config(g_is_asan); - return; + return init_default_namespace_no_config(g_is_asan); } const auto& namespace_configs = config->namespace_configs(); @@ -3514,10 +3543,17 @@ void init_default_namespace(const char* executable_path) { soinfo* ld_android_so = solist_get_head(); for (auto it : namespaces) { it.second->add_soinfo(ld_android_so); - // TODO (dimitry): somain and ld_preloads should probably be added to all of these namespaces too? + // somain and ld_preloads are added to these namespaces after LD_PRELOAD libs are linked } set_application_target_sdk_version(config->target_sdk_version()); + + std::vector<android_namespace_t*> created_namespaces; + created_namespaces.reserve(namespaces.size()); + for (auto kv : namespaces) { + created_namespaces.push_back(kv.second); + } + return created_namespaces; } // This function finds a namespace exported in ld.config.txt by its name. |