blob: c1689304505d919db7af7184c31b16391adf9bd6 [file] [log] [blame]
// 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