Index: third_party/crazy_linker/crazy_linker/src/crazy_linker_wrappers.cpp |
diff --git a/third_party/crazy_linker/crazy_linker/src/crazy_linker_wrappers.cpp b/third_party/crazy_linker/crazy_linker/src/crazy_linker_wrappers.cpp |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d1cf35ba73b3adea61a0ffa4b24c0cd6f0147930 |
--- /dev/null |
+++ b/third_party/crazy_linker/crazy_linker/src/crazy_linker_wrappers.cpp |
@@ -0,0 +1,281 @@ |
+// Copyright (c) 2013 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_wrappers.h" |
+ |
+#include <dlfcn.h> |
+#include <link.h> |
+ |
+#include "crazy_linker_debug.h" |
+#include "crazy_linker_globals.h" |
+#include "crazy_linker_library_list.h" |
+#include "crazy_linker_library_view.h" |
+#include "crazy_linker_shared_library.h" |
+#include "crazy_linker_thread.h" |
+#include "crazy_linker_util.h" |
+ |
+#ifdef __arm__ |
+// On ARM, this function is exported by the dynamic linker but never |
+// declared in any official header. It is used at runtime to |
+// find the base address of the .ARM.exidx section for the |
+// shared library containing the instruction at |pc|, as well as |
+// the number of 8-byte entries in that section, written into |*pcount| |
+extern "C" _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr, int*); |
+#else |
+// On other architectures, this function is exported by the dynamic linker |
+// but never declared in any official header. It is used at runtime to |
+// iterate over all loaded libraries and call the |cb|. When the function |
+// returns non-0, the iteration returns and the function returns its |
+// value. |
+extern "C" int dl_iterate_phdr( |
+ int (*cb)(dl_phdr_info *info, size_t size, void *data), |
+ void *data); |
+#endif |
+ |
+namespace crazy { |
+ |
+namespace { |
+ |
+#ifdef __arm__ |
+extern "C" int __cxa_atexit(void(*)(void*), void*, void*); |
+ |
+// On ARM, this function is defined as a weak symbol by libc.so. |
+// Unfortunately its address cannot be found through dlsym(), which will |
+// always return NULL. To work-around this, define a copy here that does |
+// exactly the same thing. The ARM EABI mandates the function's behaviour. |
+// __cxa_atexit() is implemented by the C library, but not declared by |
+// any official header. It's part of the low-level C++ support runtime. |
+int |
+__aeabi_atexit (void *object, void (*destructor) (void *), void *dso_handle) |
+{ |
+ return __cxa_atexit(destructor, object, dso_handle); |
+} |
+#endif |
+ |
+// Used to save the system dlerror() into our thread-specific data. |
+void SaveSystemError() { |
+ ThreadData* data = GetThreadData(); |
+ data->SetError(::dlerror()); |
+} |
+ |
+char* WrapDlerror() { |
+ ThreadData* data = GetThreadData(); |
+ const char* error = data->GetError(); |
+ data->SwapErrorBuffers(); |
+ // dlerror() returns a 'char*', but no sane client code should ever |
+ // try to write to this location. |
+ return const_cast<char*>(error); |
+} |
+ |
+void* WrapDlopen(const char* path, int mode) { |
+ |
+ ScopedGlobalLock lock; |
bulach
2013/09/10 14:21:39
nit: indent
digit1
2013/09/10 16:40:28
Done, but I'll submit a single patch that "git cl
|
+ |
+ // NOTE: If |path| is NULL, the wrapper should return a handle |
+ // corresponding to the current executable. This can't be a crazy |
+ // library, so don't try to handle it with the crazy linker. |
+ if (path) { |
+ LibraryList* lib_list = Globals::GetLibraries(); |
+ Error error; |
+ LibraryView* wrap = lib_list->LoadLibrary(path, |
+ mode, |
+ 0U /* load_address */, |
+ 0U /* file_offset */, |
+ Globals::GetSearchPaths(), |
+ &error); |
+ if (wrap) |
+ return wrap; |
+ } |
+ |
+ // Try to load the executable with the system dlopen() instead. |
+ ::dlerror(); |
+ void* system_lib = ::dlopen(path, mode); |
+ if (system_lib == NULL) { |
+ SaveSystemError(); |
+ return NULL; |
+ } |
+ |
+ LibraryView* wrap_lib = new LibraryView(); |
+ wrap_lib->SetSystem(system_lib, path ? path : "<executable>"); |
+ Globals::GetLibraries()->AddLibrary(wrap_lib); |
+ return wrap_lib; |
+} |
+ |
+void* WrapDlsym(void* lib_handle, const char* symbol_name) { |
+ LibraryView* wrap_lib = reinterpret_cast<LibraryView*>(lib_handle); |
+ |
+ if (!symbol_name) { |
+ SetLinkerError("dlsym: NULL symbol name"); |
+ return NULL; |
+ } |
+ |
+ if (!lib_handle) { |
+ SetLinkerError("dlsym: NULL library handle"); |
+ return NULL; |
+ } |
+ |
+ // TODO(digit): Handle RTLD_DEFAULT / RTLD_NEXT |
+ if (lib_handle == RTLD_DEFAULT || lib_handle == RTLD_NEXT) { |
+ SetLinkerError("dlsym: RTLD_DEFAULT/RTLD_NEXT are not implemented!"); |
+ return NULL; |
+ } |
+ |
+ // NOTE: The Android dlsym() only looks inside the target library, |
+ // while the GNU one will perform a breadth-first search into its |
+ // dependency tree. |
+ |
+ // This implementation performs a correct breadth-first search |
+ // when |lib_handle| corresponds to a crazy library, except that |
+ // it stops at system libraries that it depends on. |
+ |
+ void* result = NULL; |
+ |
+ if (wrap_lib->IsSystem()) { |
+ // Note: the system dlsym() only looks into the target library, |
+ // while the GNU linker performs a breadth-first search. |
+ result = ::dlsym(wrap_lib->GetSystem(), symbol_name); |
+ if (!result) { |
+ SaveSystemError(); |
+ LOG("dlsym:%s: could not find symbol '%s' from system library\n%s", |
+ wrap_lib->GetName(), symbol_name, |
+ GetThreadData()->GetError()); |
+ } |
+ return result; |
+ } |
+ |
+ if (wrap_lib->IsCrazy()) { |
+ ScopedGlobalLock lock; |
+ LibraryList* lib_list = Globals::GetLibraries(); |
+ void* addr = lib_list->FindSymbolFrom(symbol_name, wrap_lib); |
+ if (addr) |
+ return addr; |
+ |
+ SetLinkerError("dlsym: Could not find '%s' from library '%s'", |
+ symbol_name, wrap_lib->GetName()); |
+ return NULL; |
+ |
+ } |
+ |
+ SetLinkerError("dlsym: Invalid library handle %p looking for '%s'", |
+ lib_handle, symbol_name); |
+ return NULL; |
+} |
+ |
+int WrapDladdr(void* address, Dl_info* info) { |
+ // First, perform search in crazy libraries. |
+ { |
+ ScopedGlobalLock lock; |
+ LibraryList* lib_list = Globals::GetLibraries(); |
+ LibraryView* wrap = lib_list->FindLibraryForAddress(address); |
+ if (wrap && wrap->IsCrazy()) { |
+ SharedLibrary* lib = wrap->GetCrazy(); |
+ ::memset(info, 0, sizeof(*info)); |
+ info->dli_fname = lib->base_name; |
+ info->dli_fbase = reinterpret_cast<void*>(lib->base); |
+ |
+ // Determine if any symbol in the library contains the specified address. |
+ ELF::Sym* sym = lib->FindSymbolForAddress(address); |
+ if (sym != NULL) { |
+ info->dli_sname = lib->strtab + sym->st_name; |
+ info->dli_saddr = |
+ reinterpret_cast<void*>(lib->load_bias + sym->st_value); |
+ } |
+ return 0; |
+ } |
+ } |
+ // Otherwise, use system version. |
+ ::dlerror(); |
+ int ret = ::dladdr(address, info); |
+ if (ret != 0) |
+ SaveSystemError(); |
+ return ret; |
+} |
+ |
+int WrapDlclose(void* lib_handle) { |
+ LibraryView* wrap_lib = reinterpret_cast<LibraryView*>(lib_handle); |
+ if (!wrap_lib) { |
+ SetLinkerError("NULL library handle"); |
+ return -1; |
+ } |
+ |
+ if (wrap_lib->IsSystem() || wrap_lib->IsCrazy()) { |
+ ScopedGlobalLock lock; |
+ LibraryList* lib_list = Globals::GetLibraries(); |
+ lib_list->UnloadLibrary(wrap_lib); |
+ return 0; |
+ } |
+ |
+ // Invalid library handle!! |
+ SetLinkerError("Invalid library handle %p", lib_handle); |
+ return -1; |
+} |
+ |
+#ifdef __arm__ |
+_Unwind_Ptr WrapDl_unwind_find_exidx(_Unwind_Ptr pc, int *pcount) |
+{ |
+ // First lookup in crazy libraries. |
+ { |
+ ScopedGlobalLock lock; |
+ LibraryList* list = Globals::GetLibraries(); |
+ _Unwind_Ptr result = list->FindArmExIdx(pc, pcount); |
+ if (result) |
+ return result; |
+ } |
+ // Lookup in system libraries. |
+ return ::dl_unwind_find_exidx(pc, pcount); |
+} |
+#else // !__arm__ |
+int WrapDl_iterate_phdr(int (*cb)(dl_phdr_info*, size_t, void*), |
+ void* data) { |
+ // First, iterate over crazy libraries. |
+ { |
+ ScopedGlobalLock lock; |
+ LibraryList* list = Globals::GetLibraries(); |
+ int result = list->IteratePhdr(cb, data); |
+ if (result) |
+ return result; |
+ } |
+ // Then lookup through system ones. |
+ return ::dl_iterate_phdr(cb, data); |
+} |
+#endif // !__arm__ |
+ |
+} // namespace |
+ |
+void* WrapLinkerSymbol(const char* name) { |
+ // Shortcut, since all names begin with 'dl' |
+ // Take care of __aeabi_atexit on ARM though. |
+ if (name[0] != 'd' || name[1] != 'l') { |
+#ifdef __arm__ |
+ if (name[0] == '_' && !strcmp("__aeabi_atexit", name)) |
+ return reinterpret_cast<void*>(&__aeabi_atexit); |
+#endif |
+ return NULL; |
+ } |
+ |
+ static const struct { |
+ const char* name; |
+ void* address; |
+ } kSymbols[] = { |
+ { "dlopen", reinterpret_cast<void*>(&WrapDlopen) }, |
+ { "dlclose", reinterpret_cast<void*>(&WrapDlclose) }, |
+ { "dlerror", reinterpret_cast<void*>(&WrapDlerror) }, |
+ { "dlsym", reinterpret_cast<void*>(&WrapDlsym) }, |
+ { "dladdr", reinterpret_cast<void*>(&WrapDladdr) }, |
+#ifdef __arm__ |
+ { "dl_unwind_find_exidx", |
+ reinterpret_cast<void*>(&WrapDl_unwind_find_exidx) }, |
+#else |
+ { "dl_iterate_phdr", reinterpret_cast<void*>(&WrapDl_iterate_phdr) }, |
+#endif |
+ }; |
+ static const size_t kCount = sizeof(kSymbols)/sizeof(kSymbols[0]); |
+ for (size_t n = 0; n < kCount; ++n) { |
+ if (!strcmp(kSymbols[n].name, name)) |
+ return kSymbols[n].address; |
+ } |
+ return NULL; |
+} |
+ |
+} // namespace crazy |