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
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 8e7a141..a212624 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -558,9 +558,10 @@
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 @@
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 @@
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 @@
// 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 @@
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 @@
}
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_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 @@
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 @@
// 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 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 @@
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 @@
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 @@
}
}
- // 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 5: link libraries.
+ // 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 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 @@
}
});
+ 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 @@
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 @@
rtld_flags,
extinfo,
false /* add_as_children */,
- true /* search_linked_namespaces */)) {
+ true /* search_linked_namespaces */,
+ readers_map)) {
return nullptr;
}
@@ -2208,7 +2224,7 @@
}
} 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 @@
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 @@
}
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 @@
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 @@
}
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 @@
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.