Fix dlsym(3) to do breadth first search.
dlsym(3) with handle != RTLD_DEFAULT|RTLD_NEXT performs
breadth first search through the dependency tree.
Bug: 16653281
Change-Id: I017a6975d1a62abb0218a7eb59ae4deba458e324
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 59b9938..f8b35d7 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -469,6 +469,10 @@
}
}
+ TRACE_TYPE(LOOKUP, "NOT FOUND %s in %s@%p %x %zd",
+ name, si->name, reinterpret_cast<void*>(si->base), hash, hash % si->nbucket);
+
+
return NULL;
}
@@ -585,18 +589,43 @@
return NULL;
}
-/* This is used by dlsym(3). It performs symbol lookup only within the
- specified soinfo object and not in any of its dependencies.
+// Another soinfo list allocator to use in dlsym. We don't reuse
+// SoinfoListAllocator because it is write-protected most of the time.
+static LinkerAllocator<LinkedListEntry<soinfo>> g_soinfo_list_allocator_rw;
+class SoinfoListAllocatorRW {
+ public:
+ static LinkedListEntry<soinfo>* alloc() {
+ return g_soinfo_list_allocator_rw.alloc();
+ }
- TODO: Only looking in the specified soinfo seems wrong. dlsym(3) says
- that it should do a breadth first search through the dependency
- tree. This agrees with the ELF spec (aka System V Application
- Binary Interface) where in Chapter 5 it discuss resolving "Shared
- Object Dependencies" in breadth first search order.
- */
-ElfW(Sym)* dlsym_handle_lookup(soinfo* si, const char* name, soinfo* caller) {
- return soinfo_elf_lookup(si, elfhash(name), name,
- caller == si ? SymbolLookupScope::kAllowLocal : SymbolLookupScope::kExcludeLocal);
+ static void free(LinkedListEntry<soinfo>* ptr) {
+ g_soinfo_list_allocator_rw.free(ptr);
+ }
+};
+
+// This is used by dlsym(3). It performs symbol lookup only within the
+// specified soinfo object and its dependencies in breadth first order.
+ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found, const char* name, soinfo* caller) {
+ LinkedList<soinfo, SoinfoListAllocatorRW> visit_list;
+ visit_list.push_back(si);
+ soinfo* current_soinfo;
+ while ((current_soinfo = visit_list.pop_front()) != nullptr) {
+ ElfW(Sym)* result = soinfo_elf_lookup(current_soinfo, elfhash(name), name,
+ caller == current_soinfo ? SymbolLookupScope::kAllowLocal : SymbolLookupScope::kExcludeLocal);
+
+ if (result != nullptr) {
+ *found = current_soinfo;
+ visit_list.clear();
+ return result;
+ }
+
+ current_soinfo->get_children().for_each([&](soinfo* child) {
+ visit_list.push_back(child);
+ });
+ }
+
+ visit_list.clear();
+ return nullptr;
}
/* This is used by dlsym(3) to performs a global symbol lookup. If the