Remove dangling links in secondary namespaces
linker didn't remove link to the soinfo from shared
namespaces on soinfo_unload, because it didn't keep
record of all namespaces the library is added to.
This change adds test for this and also fixes the
problem by introducing list of secondary namespaces
to soinfo, which is used to remove soinfo in
soinfo::remove_all_links().
Bug: http://b/28115950
Change-Id: Ifbf6e54f92fa6e88f86b6a8dd6dc22d4553afd22
diff --git a/linker/linker.cpp b/linker/linker.cpp
index f3370ec..77f5359 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -110,6 +110,7 @@
void add_soinfos(const soinfo::soinfo_list_t& soinfos) {
for (auto si : soinfos) {
add_soinfo(si);
+ si->add_secondary_namespace(this);
}
}
@@ -146,6 +147,7 @@
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 soinfo* solist;
static soinfo* sonext;
@@ -286,6 +288,14 @@
g_soinfo_links_allocator.free(entry);
}
+LinkedListEntry<android_namespace_t>* NamespaceListAllocator::alloc() {
+ return g_namespace_list_allocator.alloc();
+}
+
+void NamespaceListAllocator::free(LinkedListEntry<android_namespace_t>* entry) {
+ g_namespace_list_allocator.free(entry);
+}
+
static soinfo* soinfo_alloc(android_namespace_t* ns, const char* name,
struct stat* file_stat, off64_t file_offset,
uint32_t rtld_flags) {
@@ -349,9 +359,6 @@
sonext = prev;
}
- // remove from the namespace
- si->get_namespace()->remove_soinfo(si);
-
si->~soinfo();
g_soinfo_allocator.free(si);
}
@@ -843,7 +850,7 @@
}
this->rtld_flags_ = rtld_flags;
- this->namespace_ = ns;
+ this->primary_namespace_ = ns;
}
soinfo::~soinfo() {
@@ -1003,6 +1010,7 @@
g_soinfo_allocator.protect_all(protection);
g_soinfo_links_allocator.protect_all(protection);
g_namespace_allocator.protect_all(protection);
+ g_namespace_list_allocator.protect_all(protection);
}
static size_t ref_count_;
@@ -2122,7 +2130,7 @@
TRACE("deprecated (old format of soinfo): %s needs to unload %s",
si->get_realpath(), library_name);
- soinfo* needed = find_library(si->get_namespace(),
+ soinfo* needed = find_library(si->get_primary_namespace(),
library_name, RTLD_NOLOAD, nullptr, nullptr);
if (needed != nullptr) {
@@ -2172,6 +2180,10 @@
return std::string(sym_name) + ", version " + sym_ver;
}
+static android_namespace_t* get_caller_namespace(soinfo* caller) {
+ return caller != nullptr ? caller->get_primary_namespace() : g_anonymous_namespace;
+}
+
void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) {
// Use basic string manipulation calls to avoid snprintf.
// snprintf indirectly calls pthread_getspecific to get the size of a buffer.
@@ -2208,7 +2220,7 @@
return nullptr;
}
- android_namespace_t* ns = caller != nullptr ? caller->get_namespace() : g_anonymous_namespace;
+ android_namespace_t* ns = get_caller_namespace(caller);
if (extinfo != nullptr) {
if ((extinfo->flags & ~(ANDROID_DLEXT_VALID_FLAG_BITS)) != 0) {
@@ -2302,7 +2314,7 @@
soinfo* found = nullptr;
const ElfW(Sym)* sym = nullptr;
soinfo* caller = find_containing_library(caller_addr);
- android_namespace_t* ns = caller != nullptr ? caller->get_namespace() : g_anonymous_namespace;
+ android_namespace_t* ns = get_caller_namespace(caller);
version_info vi_instance;
version_info* vi = nullptr;
@@ -2415,7 +2427,7 @@
soinfo* caller_soinfo = find_containing_library(caller_addr);
android_namespace_t* caller_ns = caller_soinfo != nullptr ?
- caller_soinfo->get_namespace() :
+ caller_soinfo->get_primary_namespace() :
g_anonymous_namespace;
ProtectedDataGuard guard;
@@ -3050,9 +3062,20 @@
});
});
- // 2. Once everything untied - clear local lists.
+ // 2. Remove from the primary namespace
+ primary_namespace_->remove_soinfo(this);
+ primary_namespace_ = nullptr;
+
+ // 3. Remove from secondary namespaces
+ secondary_namespaces_.for_each([&](android_namespace_t* ns) {
+ ns->remove_soinfo(this);
+ });
+
+
+ // 4. Once everything untied - clear local lists.
parents_.clear();
children_.clear();
+ secondary_namespaces_.clear();
}
dev_t soinfo::get_st_dev() const {
@@ -3186,14 +3209,19 @@
return g_empty_runpath;
}
-android_namespace_t* soinfo::get_namespace() {
+android_namespace_t* soinfo::get_primary_namespace() {
if (has_min_version(3)) {
- return namespace_;
+ return primary_namespace_;
}
return &g_default_namespace;
}
+void soinfo::add_secondary_namespace(android_namespace_t* secondary_ns) {
+ CHECK(has_min_version(3));
+ secondary_namespaces_.push_back(secondary_ns);
+}
+
ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const {
if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) {
return call_ifunc_resolver(s->st_value + load_bias);