summaryrefslogtreecommitdiff
path: root/linker/linker.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'linker/linker.cpp')
-rw-r--r--linker/linker.cpp252
1 files changed, 132 insertions, 120 deletions
diff --git a/linker/linker.cpp b/linker/linker.cpp
index abfd00f09..4ad44faa0 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -328,9 +328,7 @@ static void soinfo_free(soinfo* si) {
TRACE("name %s: freeing soinfo @ %p", si->get_realpath(), si);
if (!solist_remove_soinfo(si)) {
- // TODO (dimitry): revisit this - for now preserving the logic
- // but it does not look right, abort if soinfo is not in the list instead?
- return;
+ async_safe_fatal("soinfo=%p is not in soinfo_list (double unload?)", si);
}
// clear links to/from si
@@ -718,13 +716,11 @@ enum walk_action_result_t : uint32_t {
// walk_dependencies_tree returns false if walk was terminated
// by the action and true otherwise.
template<typename F>
-static bool walk_dependencies_tree(soinfo* root_soinfos[], size_t root_soinfos_size, F action) {
+static bool walk_dependencies_tree(soinfo* root_soinfo, F action) {
SoinfoLinkedList visit_list;
SoinfoLinkedList visited;
- for (size_t i = 0; i < root_soinfos_size; ++i) {
- visit_list.push_back(root_soinfos[i]);
- }
+ visit_list.push_back(root_soinfo);
soinfo* si;
while ((si = visit_list.pop_front()) != nullptr) {
@@ -760,7 +756,7 @@ static const ElfW(Sym)* dlsym_handle_lookup(android_namespace_t* ns,
const ElfW(Sym)* result = nullptr;
bool skip_lookup = skip_until != nullptr;
- walk_dependencies_tree(&root, 1, [&](soinfo* current_soinfo) {
+ walk_dependencies_tree(root, [&](soinfo* current_soinfo) {
if (skip_lookup) {
skip_lookup = current_soinfo != skip_until;
return kWalkContinue;
@@ -1478,7 +1474,6 @@ static bool find_library_internal(android_namespace_t* ns,
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;
@@ -1491,7 +1486,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);
static void shuffle(std::vector<LoadTask*>* v) {
for (size_t i = 0, size = v->size(); i < size; ++i) {
@@ -1516,9 +1510,9 @@ bool find_libraries(android_namespace_t* ns,
const android_dlextinfo* extinfo,
bool add_as_children,
bool search_linked_namespaces,
- std::unordered_map<const soinfo*, ElfReader>& readers_map,
std::vector<android_namespace_t*>* namespaces) {
// Step 0: prepare.
+ std::unordered_map<const soinfo*, ElfReader> readers_map;
LoadTaskList load_tasks;
for (size_t i = 0; i < library_names_count; ++i) {
@@ -1548,11 +1542,6 @@ bool find_libraries(android_namespace_t* ns,
}
});
- auto failure_guard = android::base::make_scope_guard([&]() {
- // Housekeeping
- soinfo_unload(soinfos, soinfos_count);
- });
-
ZipArchiveCache zip_archive_cache;
// Step 1: expand the list of load_tasks to include
@@ -1565,7 +1554,6 @@ bool find_libraries(android_namespace_t* ns,
task->set_extinfo(is_dt_needed ? nullptr : extinfo);
task->set_dt_needed(is_dt_needed);
- // 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
@@ -1583,10 +1571,6 @@ bool find_libraries(android_namespace_t* ns,
if (is_dt_needed) {
needed_by->add_child(si);
-
- if (si->is_linked()) {
- si->increment_ref_count();
- }
}
// When ld_preloads is not null, the first
@@ -1662,77 +1646,116 @@ bool find_libraries(android_namespace_t* 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.
+ // Step 5: Collect roots of local_groups.
+ // Whenever needed_by->si link crosses a namespace boundary it forms its own local_group.
+ // Here we collect new roots to link them separately later on. Note that we need to avoid
+ // collecting duplicates. Also the order is important. They need to be linked in the same
+ // BFS order we link individual libraries.
+ std::vector<soinfo*> local_group_roots;
+ if (start_with != nullptr && add_as_children) {
+ local_group_roots.push_back(start_with);
+ } else {
+ CHECK(soinfos_count == 1);
+ local_group_roots.push_back(soinfos[0]);
+ }
+
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;
+ soinfo* needed_by = task->get_needed_by();
+ bool is_dt_needed = needed_by != nullptr && (needed_by != start_with || add_as_children);
+ android_namespace_t* needed_by_ns =
+ is_dt_needed ? needed_by->get_primary_namespace() : ns;
+
+ if (!si->is_linked() && si->get_primary_namespace() != needed_by_ns) {
+ auto it = std::find(local_group_roots.begin(), local_group_roots.end(), si);
+ LD_LOG(kLogDlopen,
+ "Crossing namespace boundary (si=%s@%p, si_ns=%s@%p, needed_by=%s@%p, ns=%s@%p, needed_by_ns=%s@%p) adding to local_group_roots: %s",
+ si->get_realpath(),
+ si,
+ si->get_primary_namespace()->get_name(),
+ si->get_primary_namespace(),
+ needed_by == nullptr ? "(nullptr)" : needed_by->get_realpath(),
+ needed_by,
+ ns->get_name(),
+ ns,
+ needed_by_ns->get_name(),
+ needed_by_ns,
+ it == local_group_roots.end() ? "yes" : "no");
+
+ if (it == local_group_roots.end()) {
+ local_group_roots.push_back(si);
}
}
}
- // Step 6: link libraries in this namespace
- soinfo_list_t local_group;
- walk_dependencies_tree(
- (start_with != nullptr && add_as_children) ? &start_with : soinfos,
- (start_with != nullptr && add_as_children) ? 1 : soinfos_count,
+ // Step 6: Link all local groups
+ for (auto root : local_group_roots) {
+ soinfo_list_t local_group;
+ android_namespace_t* local_group_ns = root->get_primary_namespace();
+
+ walk_dependencies_tree(root,
[&] (soinfo* si) {
- if (ns->is_accessible(si)) {
- local_group.push_back(si);
- return kWalkContinue;
- } else {
- return kWalkSkip;
- }
- });
+ if (local_group_ns->is_accessible(si)) {
+ local_group.push_back(si);
+ return kWalkContinue;
+ } else {
+ return kWalkSkip;
+ }
+ });
- 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) ||
- !get_cfi_shadow()->AfterLoad(si, solist_get_head())) {
- return false;
+ soinfo_list_t global_group = local_group_ns->get_global_group();
+ bool linked = local_group.visit([&](soinfo* si) {
+ // Even though local group may contain accessible soinfos from other namesapces
+ // we should avoid linking them (because if they are not linked -> they
+ // are in the local_group_roots and will be linked later).
+ if (!si->is_linked() && si->get_primary_namespace() == local_group_ns) {
+ if (!si->link_image(global_group, local_group, extinfo) ||
+ !get_cfi_shadow()->AfterLoad(si, solist_get_head())) {
+ return false;
+ }
}
+
+ return true;
+ });
+
+ if (!linked) {
+ return false;
}
+ }
- return true;
- });
+ // Step 7: Mark all load_tasks as linked and increment refcounts
+ // for references between load_groups (at this point it does not matter if
+ // referenced load_groups were loaded by previous dlopen or as part of this
+ // one on step 6)
+ if (start_with != nullptr && add_as_children) {
+ start_with->set_linked();
+ }
- if (linked) {
- local_group.for_each([](soinfo* si) {
- if (!si->is_linked()) {
- si->set_linked();
- }
- });
+ for (auto&& task : load_tasks) {
+ soinfo* si = task->get_soinfo();
+ si->set_linked();
+ }
- failure_guard.Disable();
+ for (auto&& task : load_tasks) {
+ soinfo* si = task->get_soinfo();
+ soinfo* needed_by = task->get_needed_by();
+ if (needed_by != nullptr &&
+ needed_by != start_with &&
+ needed_by->get_local_group_root() != si->get_local_group_root()) {
+ si->increment_ref_count();
+ }
}
- return linked;
+
+ return true;
}
static soinfo* find_library(android_namespace_t* ns,
const char* name, int rtld_flags,
const android_dlextinfo* extinfo,
soinfo* needed_by) {
- soinfo* si;
+ soinfo* si = nullptr;
- // 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,
@@ -1745,8 +1768,10 @@ static soinfo* find_library(android_namespace_t* ns,
rtld_flags,
extinfo,
false /* add_as_children */,
- true /* search_linked_namespaces */,
- readers_map)) {
+ true /* search_linked_namespaces */)) {
+ if (si != nullptr) {
+ soinfo_unload(si);
+ }
return nullptr;
}
@@ -1755,18 +1780,26 @@ static soinfo* find_library(android_namespace_t* ns,
return si;
}
-static void soinfo_unload(soinfo* si) {
- soinfo* root = si->is_linked() ? si->get_local_group_root() : si;
+static void soinfo_unload(soinfo* unload_si) {
+ // Note that the library can be loaded but not linked;
+ // in which case there is no root but we still need
+ // to walk the tree and unload soinfos involved.
+ //
+ // This happens on unsuccessful dlopen, when one of
+ // the DT_NEEDED libraries could not be linked/found.
+ bool is_linked = unload_si->is_linked();
+ soinfo* root = is_linked ? unload_si->get_local_group_root() : unload_si;
LD_LOG(kLogDlopen,
"... dlclose(realpath=\"%s\"@%p) ... load group root is \"%s\"@%p",
- si->get_realpath(),
- si,
+ unload_si->get_realpath(),
+ unload_si,
root->get_realpath(),
root);
ScopedTrace trace((std::string("unload ") + root->get_realpath()).c_str());
+ size_t ref_count = is_linked ? root->decrement_ref_count() : 0;
if (!root->can_unload()) {
LD_LOG(kLogDlopen,
"... dlclose(root=\"%s\"@%p) ... not unloading - the load group is flagged with NODELETE",
@@ -1775,48 +1808,17 @@ static void soinfo_unload(soinfo* si) {
return;
}
- soinfo_unload(&root, 1);
-}
-
-static void soinfo_unload(soinfo* soinfos[], size_t count) {
- // Note that the library can be loaded but not linked;
- // in which case there is no root but we still need
- // to walk the tree and unload soinfos involved.
- //
- // This happens on unsuccessful dlopen, when one of
- // the DT_NEEDED libraries could not be linked/found.
- if (count == 0) {
+ if (ref_count > 0) {
+ LD_LOG(kLogDlopen,
+ "... dlclose(root=\"%s\"@%p) ... not unloading - decrementing ref_count to %zd",
+ root->get_realpath(),
+ root,
+ ref_count);
return;
}
soinfo_list_t unload_list;
- for (size_t i = 0; i < count; ++i) {
- soinfo* si = soinfos[i];
-
- if (si->can_unload()) {
- size_t ref_count = si->is_linked() ? si->decrement_ref_count() : 0;
- if (ref_count == 0) {
- unload_list.push_back(si);
- } else {
- LD_LOG(kLogDlopen,
- "... dlclose(root=\"%s\"@%p) ... not unloading - decrementing ref_count to %zd",
- si->get_realpath(),
- si,
- ref_count);
- }
- } else {
- LD_LOG(kLogDlopen,
- "... dlclose(root=\"%s\"@%p) ... not unloading - the load group is flagged with NODELETE",
- si->get_realpath(),
- si);
- return;
- }
- }
-
- // This is used to identify soinfos outside of the load-group
- // note that we cannot have > 1 in the array and have any of them
- // linked. This is why we can safely use the first one.
- soinfo* root = soinfos[0];
+ unload_list.push_back(root);
soinfo_list_t local_unload_list;
soinfo_list_t external_unload_list;
@@ -1900,12 +1902,17 @@ static void soinfo_unload(soinfo* soinfos[], size_t count) {
soinfo_free(si);
}
- while ((si = external_unload_list.pop_front()) != nullptr) {
- LD_LOG(kLogDlopen,
- "... dlclose: unloading external reference \"%s\"@%p ...",
- si->get_realpath(),
- si);
- soinfo_unload(si);
+ if (is_linked) {
+ while ((si = external_unload_list.pop_front()) != nullptr) {
+ LD_LOG(kLogDlopen,
+ "... dlclose: unloading external reference \"%s\"@%p ...",
+ si->get_realpath(),
+ si);
+ soinfo_unload(si);
+ }
+ } else {
+ LD_LOG(kLogDlopen,
+ "... dlclose: unload_si was not linked - not unloading external references ...");
}
}
@@ -3368,6 +3375,10 @@ bool soinfo::prelink_image() {
bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t& local_group,
const android_dlextinfo* extinfo) {
+ if (is_image_linked()) {
+ // already linked.
+ return true;
+ }
local_group_root_ = local_group.front();
if (local_group_root_ == nullptr) {
@@ -3510,6 +3521,7 @@ bool soinfo::link_image(const soinfo_list_t& global_group, const soinfo_list_t&
}
notify_gdb_of_load(this);
+ set_image_linked();
return true;
}