blob: f92ad7a0c45277960c29e1ac42461662ac1c9d2f [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 <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"
namespace crazy {
namespace {
// 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), count_(0), has_error_(false) {
// Nothing for now
}
LibraryList::~LibraryList() {
// Invalidate crazy library list.
head_ = NULL;
// Destroy all known libraries.
while (!known_libraries_.IsEmpty()) {
LibraryView* wrap = known_libraries_.PopLast();
delete wrap;
}
}
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,
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 (IsSystemLibrary(lib_name)) {
// This is a system library, probably because we're loading the
// library as a dependency.
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,
&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, &dependencies, error))
return NULL;
// Notify GDB of load.
lib->link_map_.l_addr = lib->load_address();
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;
}
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