Index: third_party/android_crazy_linker/src/src/crazy_linker_library_list.cpp |
diff --git a/third_party/android_crazy_linker/src/src/crazy_linker_library_list.cpp b/third_party/android_crazy_linker/src/src/crazy_linker_library_list.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..0bc3689d0dbf6a3952b679edc5ed8c57b8f66a27 |
--- /dev/null |
+++ b/third_party/android_crazy_linker/src/src/crazy_linker_library_list.cpp |
@@ -0,0 +1,554 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "crazy_linker_library_list.h" |
+ |
+#include <assert.h> |
+#include <crazy_linker.h> |
+#include <dlfcn.h> |
+ |
+#include "crazy_linker_debug.h" |
+#include "crazy_linker_library_view.h" |
+#include "crazy_linker_globals.h" |
+#include "crazy_linker_rdebug.h" |
+#include "crazy_linker_shared_library.h" |
+#include "crazy_linker_system.h" |
+#include "crazy_linker_util.h" |
+#include "crazy_linker_zip.h" |
+ |
+namespace crazy { |
+ |
+namespace { |
+ |
+// From android.os.Build.VERSION_CODES.LOLLIPOP. |
+static const int SDK_VERSION_CODE_LOLLIPOP = 21; |
+ |
+// Page size for alignment in a zip file. |
+const size_t kZipAlignmentPageSize = 4096; |
+COMPILE_ASSERT(kZipAlignmentPageSize % PAGE_SIZE == 0, |
+ kZipAlignmentPageSize_must_be_a_multiple_of_PAGE_SIZE); |
+ |
+// A helper struct used when looking up symbols in libraries. |
+struct SymbolLookupState { |
+ void* found_addr; |
+ void* weak_addr; |
+ int weak_count; |
+ |
+ SymbolLookupState() : found_addr(NULL), weak_addr(NULL), weak_count(0) {} |
+ |
+ // Check a symbol entry. |
+ bool CheckSymbol(const char* symbol, SharedLibrary* lib) { |
+ const ELF::Sym* entry = lib->LookupSymbolEntry(symbol); |
+ if (!entry) |
+ return false; |
+ |
+ void* address = reinterpret_cast<void*>(lib->load_bias() + entry->st_value); |
+ |
+ // If this is a strong symbol, record it and return true. |
+ if (ELF_ST_BIND(entry->st_info) == STB_GLOBAL) { |
+ found_addr = address; |
+ return true; |
+ } |
+ // If this is a weak symbol, record the first one and |
+ // increment the weak_count. |
+ if (++weak_count == 1) |
+ weak_addr = address; |
+ |
+ return false; |
+ } |
+}; |
+ |
+} // namespace |
+ |
+LibraryList::LibraryList() : head_(0), has_error_(false) { |
+ const int sdk_build_version = *Globals::GetSDKBuildVersion(); |
+ |
+ // If SDK version is Lollipop or earlier, we need to load anything |
+ // listed in LD_PRELOAD explicitly, because dlsym() on the main executable |
+ // fails to lookup in preloads on those releases. Also, when doing our |
+ // symbol resolution we need to explicity search preloads *before* we |
+ // search the main executable, to ensure that preloads override symbols |
+ // correctly. Searching preloads before main is opposite to the way the |
+ // system linker's ordering of these searches, but it is required here to |
+ // work round the platform's dlsym() issue. |
+ // |
+ // If SDK version is Lollipop-mr1 or later then dlsym() will search |
+ // preloads when invoked on the main executable, meaning that we do not |
+ // want (or need) to load them here. The platform itself will take care |
+ // of them for us, and so by not loading preloads here our preloads list |
+ // remains empty, so that searching it for name lookups is a no-op. |
+ // |
+ // For more, see: |
+ // https://code.google.com/p/android/issues/detail?id=74255 |
+ if (sdk_build_version <= SDK_VERSION_CODE_LOLLIPOP) |
+ LoadPreloads(); |
+} |
+ |
+LibraryList::~LibraryList() { |
+ // Invalidate crazy library list. |
+ head_ = NULL; |
+ |
+ // Destroy all known libraries. |
+ while (!known_libraries_.IsEmpty()) { |
+ LibraryView* wrap = known_libraries_.PopLast(); |
+ delete wrap; |
+ } |
+} |
+ |
+void LibraryList::LoadPreloads() { |
+ const char* ld_preload = GetEnv("LD_PRELOAD"); |
+ if (!ld_preload) |
+ return; |
+ |
+ SearchPathList search_path_list; |
+ search_path_list.ResetFromEnv("LD_LIBRARY_PATH"); |
+ |
+ LOG("%s: Preloads list is: %s\n", __FUNCTION__, ld_preload); |
+ const char* current = ld_preload; |
+ const char* end = ld_preload + strlen(ld_preload); |
+ |
+ // Iterate over library names listed in the environment. The separator |
+ // here may be either space or colon. |
+ while (current < end) { |
+ const char* item = current; |
+ const size_t item_length = strcspn(current, " :"); |
+ if (item_length == 0) { |
+ current += 1; |
+ continue; |
+ } |
+ current = item + item_length + 1; |
+ |
+ String lib_name(item, item_length); |
+ LOG("%s: Attempting to preload %s\n", __FUNCTION__, lib_name.c_str()); |
+ |
+ if (FindKnownLibrary(lib_name.c_str())) { |
+ LOG("%s: already loaded %s: ignoring\n", __FUNCTION__, lib_name.c_str()); |
+ continue; |
+ } |
+ |
+ Error error; |
+ LibraryView* preload = LoadLibrary(lib_name.c_str(), |
+ RTLD_NOW | RTLD_GLOBAL, |
+ 0U /* load address */, |
+ 0U /* file offset */, |
+ &search_path_list, |
+ true /* is_dependency_or_preload */, |
+ &error); |
+ if (!preload) { |
+ LOG("'%s' cannot be preloaded: ignored\n", lib_name.c_str()); |
+ continue; |
+ } |
+ |
+ preloaded_libraries_.PushBack(preload); |
+ } |
+ |
+ if (CRAZY_DEBUG) { |
+ LOG("%s: Preloads loaded\n", __FUNCTION__); |
+ for (size_t n = 0; n < preloaded_libraries_.GetCount(); ++n) |
+ LOG(" ... %p %s\n", |
+ preloaded_libraries_[n], preloaded_libraries_[n]->GetName()); |
+ LOG(" preloads @%p\n", &preloaded_libraries_); |
+ } |
+} |
+ |
+LibraryView* LibraryList::FindLibraryByName(const char* lib_name) { |
+ // Sanity check. |
+ if (!lib_name) |
+ return NULL; |
+ |
+ for (size_t n = 0; n < known_libraries_.GetCount(); ++n) { |
+ LibraryView* wrap = known_libraries_[n]; |
+ if (!strcmp(lib_name, wrap->GetName())) |
+ return wrap; |
+ } |
+ return NULL; |
+} |
+ |
+void* LibraryList::FindSymbolFrom(const char* symbol_name, LibraryView* from) { |
+ SymbolLookupState lookup_state; |
+ |
+ if (!from) |
+ return NULL; |
+ |
+ // Use a work-queue and a set to ensure to perform a breadth-first |
+ // search. |
+ Vector<LibraryView*> work_queue; |
+ Set<LibraryView*> visited_set; |
+ |
+ work_queue.PushBack(from); |
+ |
+ while (!work_queue.IsEmpty()) { |
+ LibraryView* lib = work_queue.PopFirst(); |
+ if (lib->IsCrazy()) { |
+ if (lookup_state.CheckSymbol(symbol_name, lib->GetCrazy())) |
+ return lookup_state.found_addr; |
+ } else if (lib->IsSystem()) { |
+ // TODO(digit): Support weak symbols in system libraries. |
+ // With the current code, all symbols in system libraries |
+ // are assumed to be non-weak. |
+ void* addr = lib->LookupSymbol(symbol_name); |
+ if (addr) |
+ return addr; |
+ } |
+ |
+ // If this is a crazy library, add non-visited dependencies |
+ // to the work queue. |
+ if (lib->IsCrazy()) { |
+ SharedLibrary::DependencyIterator iter(lib->GetCrazy()); |
+ while (iter.GetNext()) { |
+ LibraryView* dependency = FindKnownLibrary(iter.GetName()); |
+ if (dependency && !visited_set.Has(dependency)) { |
+ work_queue.PushBack(dependency); |
+ visited_set.Add(dependency); |
+ } |
+ } |
+ } |
+ } |
+ |
+ if (lookup_state.weak_count >= 1) { |
+ // There was at least a single weak symbol definition, so use |
+ // the first one found in breadth-first search order. |
+ return lookup_state.weak_addr; |
+ } |
+ |
+ // There was no symbol definition. |
+ return NULL; |
+} |
+ |
+LibraryView* LibraryList::FindLibraryForAddress(void* address) { |
+ // Linearly scan all libraries, looking for one that contains |
+ // a given address. NOTE: This doesn't check that this falls |
+ // inside one of the mapped library segments. |
+ for (size_t n = 0; n < known_libraries_.GetCount(); ++n) { |
+ LibraryView* wrap = known_libraries_[n]; |
+ // TODO(digit): Search addresses inside system libraries. |
+ if (wrap->IsCrazy()) { |
+ SharedLibrary* lib = wrap->GetCrazy(); |
+ if (lib->ContainsAddress(address)) |
+ return wrap; |
+ } |
+ } |
+ return NULL; |
+} |
+ |
+#ifdef __arm__ |
+_Unwind_Ptr LibraryList::FindArmExIdx(void* pc, int* count) { |
+ for (SharedLibrary* lib = head_; lib; lib = lib->list_next_) { |
+ if (lib->ContainsAddress(pc)) { |
+ *count = static_cast<int>(lib->arm_exidx_count_); |
+ return reinterpret_cast<_Unwind_Ptr>(lib->arm_exidx_); |
+ } |
+ } |
+ *count = 0; |
+ return NULL; |
+} |
+#else // !__arm__ |
+int LibraryList::IteratePhdr(PhdrIterationCallback callback, void* data) { |
+ int result = 0; |
+ for (SharedLibrary* lib = head_; lib; lib = lib->list_next_) { |
+ dl_phdr_info info; |
+ info.dlpi_addr = lib->link_map_.l_addr; |
+ info.dlpi_name = lib->link_map_.l_name; |
+ info.dlpi_phdr = lib->phdr(); |
+ info.dlpi_phnum = lib->phdr_count(); |
+ result = callback(&info, sizeof(info), data); |
+ if (result) |
+ break; |
+ } |
+ return result; |
+} |
+#endif // !__arm__ |
+ |
+void LibraryList::UnloadLibrary(LibraryView* wrap) { |
+ // Sanity check. |
+ LOG("%s: for %s (ref_count=%d)\n", |
+ __FUNCTION__, |
+ wrap->GetName(), |
+ wrap->ref_count()); |
+ |
+ if (!wrap->IsSystem() && !wrap->IsCrazy()) |
+ return; |
+ |
+ if (!wrap->SafeDecrementRef()) |
+ return; |
+ |
+ // If this is a crazy library, perform manual cleanup first. |
+ if (wrap->IsCrazy()) { |
+ SharedLibrary* lib = wrap->GetCrazy(); |
+ |
+ // Remove from internal list of crazy libraries. |
+ if (lib->list_next_) |
+ lib->list_next_->list_prev_ = lib->list_prev_; |
+ if (lib->list_prev_) |
+ lib->list_prev_->list_next_ = lib->list_next_; |
+ if (lib == head_) |
+ head_ = lib->list_next_; |
+ |
+ // Call JNI_OnUnload, if necessary, then the destructors. |
+ lib->CallJniOnUnload(); |
+ lib->CallDestructors(); |
+ |
+ // Unload the dependencies recursively. |
+ SharedLibrary::DependencyIterator iter(lib); |
+ while (iter.GetNext()) { |
+ LibraryView* dependency = FindKnownLibrary(iter.GetName()); |
+ if (dependency) |
+ UnloadLibrary(dependency); |
+ } |
+ |
+ // Tell GDB of this removal. |
+ Globals::GetRDebug()->DelEntry(&lib->link_map_); |
+ } |
+ |
+ known_libraries_.Remove(wrap); |
+ |
+ // Delete the wrapper, which will delete the crazy library, or |
+ // dlclose() the system one. |
+ delete wrap; |
+} |
+ |
+LibraryView* LibraryList::LoadLibrary(const char* lib_name, |
+ int dlopen_mode, |
+ uintptr_t load_address, |
+ off_t file_offset, |
+ SearchPathList* search_path_list, |
+ bool is_dependency_or_preload, |
+ Error* error) { |
+ const char* base_name = GetBaseNamePtr(lib_name); |
+ |
+ LOG("%s: lib_name='%s'\n", __FUNCTION__, lib_name); |
+ |
+ // First check whether a library with the same base name was |
+ // already loaded. |
+ LibraryView* wrap = FindKnownLibrary(lib_name); |
+ if (wrap) { |
+ if (load_address) { |
+ // Check that this is a crazy library and that is was loaded at |
+ // the correct address. |
+ if (!wrap->IsCrazy()) { |
+ error->Format("System library can't be loaded at fixed address %08x", |
+ load_address); |
+ return NULL; |
+ } |
+ uintptr_t actual_address = wrap->GetCrazy()->load_address(); |
+ if (actual_address != load_address) { |
+ error->Format("Library already loaded at @%08x, can't load it at @%08x", |
+ actual_address, |
+ load_address); |
+ return NULL; |
+ } |
+ } |
+ wrap->AddRef(); |
+ return wrap; |
+ } |
+ |
+ // If this load is prompted by either dependencies or preloads, open |
+ // normally with dlopen() and do not proceed to try and load the library |
+ // crazily. |
+ if (is_dependency_or_preload) { |
+ LOG("%s: Loading system library '%s'\n", __FUNCTION__, lib_name); |
+ ::dlerror(); |
+ void* system_lib = dlopen(lib_name, dlopen_mode); |
+ if (!system_lib) { |
+ error->Format("Can't load system library %s: %s", lib_name, ::dlerror()); |
+ return NULL; |
+ } |
+ |
+ LibraryView* wrap = new LibraryView(); |
+ wrap->SetSystem(system_lib, lib_name); |
+ known_libraries_.PushBack(wrap); |
+ |
+ LOG("%s: System library %s loaded at %p\n", __FUNCTION__, lib_name, wrap); |
+ LOG(" name=%s\n", wrap->GetName()); |
+ return wrap; |
+ } |
+ |
+ ScopedPtr<SharedLibrary> lib(new SharedLibrary()); |
+ |
+ // Find the full library path. |
+ String full_path; |
+ |
+ if (!strchr(lib_name, '/')) { |
+ LOG("%s: Looking through the search path list\n", __FUNCTION__); |
+ const char* path = search_path_list->FindFile(lib_name); |
+ if (!path) { |
+ error->Format("Can't find library file %s", lib_name); |
+ return NULL; |
+ } |
+ full_path = path; |
+ } else { |
+ if (lib_name[0] != '/') { |
+ // Need to transform this into a full path. |
+ full_path = GetCurrentDirectory(); |
+ if (full_path.size() && full_path[full_path.size() - 1] != '/') |
+ full_path += '/'; |
+ full_path += lib_name; |
+ } else { |
+ // Absolute path. Easy. |
+ full_path = lib_name; |
+ } |
+ LOG("%s: Full library path: %s\n", __FUNCTION__, full_path.c_str()); |
+ if (!PathIsFile(full_path.c_str())) { |
+ error->Format("Library file doesn't exist: %s", full_path.c_str()); |
+ return NULL; |
+ } |
+ } |
+ |
+ // Load the library |
+ if (!lib->Load(full_path.c_str(), load_address, file_offset, error)) |
+ return NULL; |
+ |
+ // Load all dependendent libraries. |
+ LOG("%s: Loading dependencies of %s\n", __FUNCTION__, base_name); |
+ SharedLibrary::DependencyIterator iter(lib.Get()); |
+ Vector<LibraryView*> dependencies; |
+ while (iter.GetNext()) { |
+ Error dep_error; |
+ LibraryView* dependency = LoadLibrary(iter.GetName(), |
+ dlopen_mode, |
+ 0U /* load address */, |
+ 0U /* file offset */, |
+ search_path_list, |
+ true /* is_dependency_or_preload */, |
+ &dep_error); |
+ if (!dependency) { |
+ error->Format("When loading %s: %s", base_name, dep_error.c_str()); |
+ return NULL; |
+ } |
+ dependencies.PushBack(dependency); |
+ } |
+ if (CRAZY_DEBUG) { |
+ LOG("%s: Dependencies loaded for %s\n", __FUNCTION__, base_name); |
+ for (size_t n = 0; n < dependencies.GetCount(); ++n) |
+ LOG(" ... %p %s\n", dependencies[n], dependencies[n]->GetName()); |
+ LOG(" dependencies @%p\n", &dependencies); |
+ } |
+ |
+ // Relocate the library. |
+ LOG("%s: Relocating %s\n", __FUNCTION__, base_name); |
+ if (!lib->Relocate(this, &preloaded_libraries_, &dependencies, error)) |
+ return NULL; |
+ |
+ // Notify GDB of load. |
+ lib->link_map_.l_addr = lib->load_bias(); |
+ lib->link_map_.l_name = const_cast<char*>(lib->base_name_); |
+ lib->link_map_.l_ld = reinterpret_cast<uintptr_t>(lib->view_.dynamic()); |
+ Globals::GetRDebug()->AddEntry(&lib->link_map_); |
+ |
+ // The library was properly loaded, add it to the list of crazy |
+ // libraries. IMPORTANT: Do this _before_ calling the constructors |
+ // because these could call dlopen(). |
+ lib->list_next_ = head_; |
+ lib->list_prev_ = NULL; |
+ if (head_) |
+ head_->list_prev_ = lib.Get(); |
+ head_ = lib.Get(); |
+ |
+ // Then create a new LibraryView for it. |
+ wrap = new LibraryView(); |
+ wrap->SetCrazy(lib.Get(), lib_name); |
+ known_libraries_.PushBack(wrap); |
+ |
+ LOG("%s: Running constructors for %s\n", __FUNCTION__, base_name); |
+ |
+ // Now run the constructors. |
+ lib->CallConstructors(); |
+ |
+ LOG("%s: Done loading %s\n", __FUNCTION__, base_name); |
+ lib.Release(); |
+ |
+ return wrap; |
+} |
+ |
+// We identify the abi tag for which the linker is running. This allows |
+// us to select the library which matches the abi of the linker. |
+ |
+#if defined(__arm__) && defined(__ARM_ARCH_7A__) |
+#define CURRENT_ABI "armeabi-v7a" |
+#elif defined(__arm__) |
+#define CURRENT_ABI "armeabi" |
+#elif defined(__i386__) |
+#define CURRENT_ABI "x86" |
+#elif defined(__mips__) |
+#define CURRENT_ABI "mips" |
+#elif defined(__x86_64__) |
+#define CURRENT_ABI "x86_64" |
+#elif defined(__aarch64__) |
+#define CURRENT_ABI "arm64-v8a" |
+#else |
+#error "Unsupported target abi" |
+#endif |
+ |
+String LibraryList::GetLibraryFilePathInZipFile(const char* lib_name) { |
+ String path; |
+ path.Reserve(kMaxFilePathLengthInZip); |
+ path = "lib/"; |
+ path += CURRENT_ABI; |
+ path += "/crazy."; |
+ path += lib_name; |
+ return path; |
+} |
+ |
+int LibraryList::FindMappableLibraryInZipFile( |
+ const char* zip_file_path, |
+ const char* lib_name, |
+ Error* error) { |
+ String path = GetLibraryFilePathInZipFile(lib_name); |
+ if (path.size() >= kMaxFilePathLengthInZip) { |
+ error->Format("Filename too long for a file in a zip file %s\n", |
+ path.c_str()); |
+ return CRAZY_OFFSET_FAILED; |
+ } |
+ |
+ int offset = FindStartOffsetOfFileInZipFile(zip_file_path, path.c_str()); |
+ if (offset == CRAZY_OFFSET_FAILED) { |
+ return CRAZY_OFFSET_FAILED; |
+ } |
+ |
+ COMPILE_ASSERT((kZipAlignmentPageSize & (kZipAlignmentPageSize - 1)) == 0, |
+ kZipAlignmentPageSize_must_be_a_power_of_2); |
+ |
+ if ((offset & (kZipAlignmentPageSize - 1)) != 0) { |
+ error->Format("Library %s is not page aligned in zipfile %s\n", |
+ lib_name, zip_file_path); |
+ return CRAZY_OFFSET_FAILED; |
+ } |
+ |
+ assert(offset != CRAZY_OFFSET_FAILED); |
+ return offset; |
+} |
+ |
+LibraryView* LibraryList::LoadLibraryInZipFile( |
+ const char* zip_file_path, |
+ const char* lib_name, |
+ int dlopen_flags, |
+ uintptr_t load_address, |
+ SearchPathList* search_path_list, |
+ bool is_dependency_or_preload, |
+ Error* error) { |
+ int offset = FindMappableLibraryInZipFile(zip_file_path, lib_name, error); |
+ if (offset == CRAZY_OFFSET_FAILED) { |
+ return NULL; |
+ } |
+ |
+ return LoadLibrary( |
+ zip_file_path, dlopen_flags, load_address, offset, |
+ search_path_list, is_dependency_or_preload, error); |
+} |
+ |
+void LibraryList::AddLibrary(LibraryView* wrap) { |
+ known_libraries_.PushBack(wrap); |
+} |
+ |
+LibraryView* LibraryList::FindKnownLibrary(const char* name) { |
+ const char* base_name = GetBaseNamePtr(name); |
+ for (size_t n = 0; n < known_libraries_.GetCount(); ++n) { |
+ LibraryView* wrap = known_libraries_[n]; |
+ if (!strcmp(base_name, wrap->GetName())) |
+ return wrap; |
+ } |
+ return NULL; |
+} |
+ |
+} // namespace crazy |