| // 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 "crazy_linker_debug.h" |
| #include "crazy_linker_globals.h" |
| #include "crazy_linker_library_view.h" |
| #include "crazy_linker_pointer_set.h" |
| #include "crazy_linker_rdebug.h" |
| #include "crazy_linker_shared_library.h" |
| #include "crazy_linker_system.h" |
| #include "crazy_linker_system_linker.h" |
| #include "crazy_linker_util.h" |
| |
| #ifdef __ANDROID__ |
| #include "crazy_linker_system_android.h" |
| #endif |
| |
| namespace crazy { |
| |
| namespace { |
| |
| // A helper struct used when looking up symbols in libraries. |
| struct SymbolLookupState { |
| void* found_addr = nullptr; |
| void* weak_addr = nullptr; |
| int 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() { |
| #ifdef __ANDROID__ |
| const int sdk_build_version = GetAndroidDeviceApiLevel(); |
| |
| // 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 <= ANDROID_SDK_VERSION_CODE_LOLLIPOP) |
| LoadPreloads(); |
| #endif // __ANDROID__ |
| } |
| |
| LibraryList::~LibraryList() { |
| // Invalidate crazy library list. |
| head_ = nullptr; |
| |
| // Destroy all known libraries in reverse order. |
| while (!known_libraries_.IsEmpty()) { |
| LibraryView* view = known_libraries_.PopLast(); |
| delete view; |
| } |
| } |
| |
| void LibraryList::LoadPreloads() { |
| const char* ld_preload = GetEnv("LD_PRELOAD"); |
| if (!ld_preload) |
| return; |
| |
| LOG("Preloads list is: %s", 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("Attempting to preload %s", lib_name.c_str()); |
| |
| if (FindKnownLibrary(lib_name.c_str())) { |
| LOG("already loaded %s: ignoring", lib_name.c_str()); |
| continue; |
| } |
| |
| Error error; |
| LibraryView* preload = LoadLibraryWithSystemLinker( |
| lib_name.c_str(), RTLD_NOW | RTLD_GLOBAL, &error); |
| if (!preload) { |
| LOG("'%s' cannot be preloaded: ignored\n", lib_name.c_str()); |
| continue; |
| } |
| |
| preloaded_libraries_.PushBack(preload); |
| } |
| |
| if (CRAZY_DEBUG) { |
| LOG("Preloads loaded"); |
| for (const LibraryView* preload : preloaded_libraries_) |
| LOG(" ... %p %s\n", preload, preload->GetName()); |
| LOG(" preloads @%p\n", &preloaded_libraries_); |
| } |
| } |
| |
| LibraryView* LibraryList::FindLibraryByName(const char* lib_name) { |
| // Sanity check. |
| if (!lib_name) |
| return nullptr; |
| |
| for (LibraryView* view : known_libraries_) { |
| if (!strcmp(lib_name, view->GetName())) |
| return view; |
| } |
| return nullptr; |
| } |
| |
| void* LibraryList::FindSymbolFrom(const char* symbol_name, |
| const LibraryView* from) { |
| SymbolLookupState lookup_state; |
| |
| if (!from) |
| return nullptr; |
| |
| // Use a work-queue and a set to ensure to perform a breadth-first |
| // search. |
| Vector<const LibraryView*> work_queue; |
| PointerSet visited_set; |
| |
| work_queue.PushBack(from); |
| |
| while (!work_queue.IsEmpty()) { |
| const 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()) { |
| LibraryView::SearchResult sym = lib->LookupSymbol(symbol_name); |
| if (sym.IsValid()) |
| return sym.address; |
| } |
| |
| // 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 nullptr; |
| } |
| |
| 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 (LibraryView* view : known_libraries_) { |
| // TODO(digit): Search addresses inside system libraries. |
| if (view->IsCrazy()) { |
| SharedLibrary* lib = view->GetCrazy(); |
| if (lib->ContainsAddress(address)) |
| return view; |
| } |
| } |
| return nullptr; |
| } |
| |
| #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 static_cast<_Unwind_Ptr>(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("for %s (ref_count=%d)", 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. |
| LOG("Running JNI_OnUnload() for %s", wrap->GetName()); |
| lib->CallJniOnUnload(); |
| LOG("Running destructors for %s", wrap->GetName()); |
| 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::LoadLibraryWithSystemLinker(const char* lib_name, |
| int dlopen_mode, |
| Error* error) { |
| // First check whether a library with the same base name was |
| // already loaded. |
| LibraryView* view = FindKnownLibrary(lib_name); |
| if (view) { |
| view->AddRef(); |
| return view; |
| } |
| |
| void* system_lib = SystemLinker::Open(lib_name, dlopen_mode); |
| if (!system_lib) { |
| error->Format("Can't load system library %s: %s", lib_name, |
| SystemLinker::Error()); |
| return nullptr; |
| } |
| |
| // Can't really find the DT_SONAME of this library, assume if is its basename. |
| view = new LibraryView(system_lib, GetBaseNamePtr(lib_name)); |
| known_libraries_.PushBack(view); |
| |
| LOG("System library %s loaded at %p", lib_name, view); |
| return view; |
| } |
| |
| LibraryView* LibraryList::LoadLibrary(const char* lib_name, |
| uintptr_t load_address, |
| SearchPathList* search_path_list, |
| Error* error) { |
| const char* base_name = GetBaseNamePtr(lib_name); |
| |
| LOG("lib_name='%s'", lib_name); |
| |
| // First check whether a library with the same base name was |
| // already loaded. |
| LibraryView* view = FindKnownLibrary(base_name); |
| if (view) { |
| if (load_address) { |
| // Check that this is a crazy library and that is was loaded at |
| // the correct address. |
| if (!view->IsCrazy()) { |
| error->Format("System library can't be loaded at fixed address %08x", |
| load_address); |
| return nullptr; |
| } |
| uintptr_t actual_address = view->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 nullptr; |
| } |
| } |
| view->AddRef(); |
| return view; |
| } |
| |
| // Find the full library path. |
| String full_path; |
| |
| LOG("Looking through the search path list"); |
| SearchPathList::Result probe = search_path_list->FindFile(lib_name); |
| if (!probe.IsValid()) { |
| error->Format("Can't find library file %s", lib_name); |
| return nullptr; |
| } |
| LOG("Found library: path %s @ 0x%x", probe.path.c_str(), probe.offset); |
| |
| if (IsSystemLibraryPath(probe.path.c_str())) { |
| return LoadLibraryWithSystemLinker(probe.path.c_str(), RTLD_NOW, error); |
| } |
| |
| // Load the library with the crazy linker. |
| ScopedPtr<SharedLibrary> lib(new SharedLibrary()); |
| if (!lib->Load(probe.path.c_str(), load_address, probe.offset, error)) |
| return nullptr; |
| |
| // Load all dependendent libraries. |
| LOG("Loading dependencies of %s", base_name); |
| SharedLibrary::DependencyIterator iter(lib.Get()); |
| Vector<LibraryView*> dependencies; |
| while (iter.GetNext()) { |
| Error dep_error; |
| // TODO(digit): Call LoadLibrary recursively instead when properly |
| // detecting system vs Chromium libraries (http://crbug.com/843987). |
| LibraryView* dependency = |
| LoadLibraryWithSystemLinker(iter.GetName(), RTLD_NOW, &dep_error); |
| if (!dependency) { |
| error->Format("When loading %s: %s", base_name, dep_error.c_str()); |
| return nullptr; |
| } |
| dependencies.PushBack(dependency); |
| } |
| if (CRAZY_DEBUG) { |
| LOG("Dependencies loaded for %s", base_name); |
| for (const LibraryView* dep : dependencies) |
| LOG(" ... %p %s\n", dep, dep->GetName()); |
| LOG(" dependencies @%p\n", &dependencies); |
| } |
| |
| // Relocate the library. |
| LOG("Relocating %s", base_name); |
| if (!lib->Relocate(this, &preloaded_libraries_, &dependencies, error)) |
| return nullptr; |
| |
| // 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_ = nullptr; |
| if (head_) |
| head_->list_prev_ = lib.Get(); |
| head_ = lib.Get(); |
| |
| // Then create a new LibraryView for it. |
| view = new LibraryView(lib.Release()); |
| known_libraries_.PushBack(view); |
| |
| LOG("Running constructors for %s", base_name); |
| // Now run the constructors. |
| view->GetCrazy()->CallConstructors(); |
| |
| // Then try to call JNI_OnLoad() if necessary. |
| LOG("Running JNI_OnLoad() for %s", base_name); |
| Globals* globals = Globals::Get(); |
| if (!view->GetCrazy()->CallJniOnLoad(globals->java_vm(), |
| globals->minimum_jni_version(), error)) { |
| LOG("Error on JNI_OnLoad(): %s", error->c_str()); |
| UnloadLibrary(view); |
| return nullptr; |
| } |
| |
| LOG("Done loading %s", base_name); |
| return view; |
| } |
| |
| void LibraryList::AddLibrary(LibraryView* wrap) { |
| known_libraries_.PushBack(wrap); |
| } |
| |
| LibraryView* LibraryList::FindKnownLibrary(const char* name) { |
| const char* base_name = GetBaseNamePtr(name); |
| for (LibraryView* view : known_libraries_) { |
| if (!strcmp(base_name, view->GetName())) |
| return view; |
| } |
| return nullptr; |
| } |
| |
| } // namespace crazy |