Home | History | Annotate | Download | only in src
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "crazy_linker_wrappers.h"
      6 
      7 #include <dlfcn.h>
      8 #include <link.h>
      9 
     10 #include "crazy_linker_debug.h"
     11 #include "crazy_linker_globals.h"
     12 #include "crazy_linker_library_list.h"
     13 #include "crazy_linker_library_view.h"
     14 #include "crazy_linker_shared_library.h"
     15 #include "crazy_linker_thread.h"
     16 #include "crazy_linker_util.h"
     17 
     18 #ifdef __arm__
     19 // On ARM, this function is exported by the dynamic linker but never
     20 // declared in any official header. It is used at runtime to
     21 // find the base address of the .ARM.exidx section for the
     22 // shared library containing the instruction at |pc|, as well as
     23 // the number of 8-byte entries in that section, written into |*pcount|
     24 extern "C" _Unwind_Ptr dl_unwind_find_exidx(_Unwind_Ptr, int*);
     25 #else
     26 // On other architectures, this function is exported by the dynamic linker
     27 // but never declared in any official header. It is used at runtime to
     28 // iterate over all loaded libraries and call the |cb|. When the function
     29 // returns non-0, the iteration returns and the function returns its
     30 // value.
     31 extern "C" int dl_iterate_phdr(int (*cb)(dl_phdr_info* info,
     32                                          size_t size,
     33                                          void* data),
     34                                void* data);
     35 #endif
     36 
     37 namespace crazy {
     38 
     39 namespace {
     40 
     41 #ifdef __arm__
     42 extern "C" int __cxa_atexit(void (*)(void*), void*, void*);
     43 
     44 // On ARM, this function is defined as a weak symbol by libc.so.
     45 // Unfortunately its address cannot be found through dlsym(), which will
     46 // always return NULL. To work-around this, define a copy here that does
     47 // exactly the same thing. The ARM EABI mandates the function's behaviour.
     48 // __cxa_atexit() is implemented by the C library, but not declared by
     49 // any official header. It's part of the low-level C++ support runtime.
     50 int __aeabi_atexit(void* object, void (*destructor)(void*), void* dso_handle) {
     51   return __cxa_atexit(destructor, object, dso_handle);
     52 }
     53 #endif
     54 
     55 // Used to save the system dlerror() into our thread-specific data.
     56 void SaveSystemError() {
     57   ThreadData* data = GetThreadData();
     58   data->SetError(::dlerror());
     59 }
     60 
     61 char* WrapDlerror() {
     62   ThreadData* data = GetThreadData();
     63   const char* error = data->GetError();
     64   data->SwapErrorBuffers();
     65   // dlerror() returns a 'char*', but no sane client code should ever
     66   // try to write to this location.
     67   return const_cast<char*>(error);
     68 }
     69 
     70 void* WrapDlopen(const char* path, int mode) {
     71   ScopedGlobalLock lock;
     72 
     73   // NOTE: If |path| is NULL, the wrapper should return a handle
     74   // corresponding to the current executable. This can't be a crazy
     75   // library, so don't try to handle it with the crazy linker.
     76   if (path) {
     77     LibraryList* lib_list = Globals::GetLibraries();
     78     Error error;
     79     LibraryView* wrap = lib_list->LoadLibrary(path,
     80                                               mode,
     81                                               0U /* load_address */,
     82                                               0U /* file_offset */,
     83                                               Globals::GetSearchPaths(),
     84                                               &error);
     85     if (wrap)
     86       return wrap;
     87   }
     88 
     89   // Try to load the executable with the system dlopen() instead.
     90   ::dlerror();
     91   void* system_lib = ::dlopen(path, mode);
     92   if (system_lib == NULL) {
     93     SaveSystemError();
     94     return NULL;
     95   }
     96 
     97   LibraryView* wrap_lib = new LibraryView();
     98   wrap_lib->SetSystem(system_lib, path ? path : "<executable>");
     99   Globals::GetLibraries()->AddLibrary(wrap_lib);
    100   return wrap_lib;
    101 }
    102 
    103 void* WrapDlsym(void* lib_handle, const char* symbol_name) {
    104   LibraryView* wrap_lib = reinterpret_cast<LibraryView*>(lib_handle);
    105 
    106   if (!symbol_name) {
    107     SetLinkerError("dlsym: NULL symbol name");
    108     return NULL;
    109   }
    110 
    111   if (!lib_handle) {
    112     SetLinkerError("dlsym: NULL library handle");
    113     return NULL;
    114   }
    115 
    116   // TODO(digit): Handle RTLD_DEFAULT / RTLD_NEXT
    117   if (lib_handle == RTLD_DEFAULT || lib_handle == RTLD_NEXT) {
    118     SetLinkerError("dlsym: RTLD_DEFAULT/RTLD_NEXT are not implemented!");
    119     return NULL;
    120   }
    121 
    122   // NOTE: The Android dlsym() only looks inside the target library,
    123   // while the GNU one will perform a breadth-first search into its
    124   // dependency tree.
    125 
    126   // This implementation performs a correct breadth-first search
    127   // when |lib_handle| corresponds to a crazy library, except that
    128   // it stops at system libraries that it depends on.
    129 
    130   void* result = NULL;
    131 
    132   if (wrap_lib->IsSystem()) {
    133     // Note: the system dlsym() only looks into the target library,
    134     // while the GNU linker performs a breadth-first search.
    135     result = ::dlsym(wrap_lib->GetSystem(), symbol_name);
    136     if (!result) {
    137       SaveSystemError();
    138       LOG("dlsym:%s: could not find symbol '%s' from system library\n%s",
    139           wrap_lib->GetName(),
    140           symbol_name,
    141           GetThreadData()->GetError());
    142     }
    143     return result;
    144   }
    145 
    146   if (wrap_lib->IsCrazy()) {
    147     ScopedGlobalLock lock;
    148     LibraryList* lib_list = Globals::GetLibraries();
    149     void* addr = lib_list->FindSymbolFrom(symbol_name, wrap_lib);
    150     if (addr)
    151       return addr;
    152 
    153     SetLinkerError("dlsym: Could not find '%s' from library '%s'",
    154                    symbol_name,
    155                    wrap_lib->GetName());
    156     return NULL;
    157   }
    158 
    159   SetLinkerError("dlsym: Invalid library handle %p looking for '%s'",
    160                  lib_handle,
    161                  symbol_name);
    162   return NULL;
    163 }
    164 
    165 int WrapDladdr(void* address, Dl_info* info) {
    166   // First, perform search in crazy libraries.
    167   {
    168     ScopedGlobalLock lock;
    169     LibraryList* lib_list = Globals::GetLibraries();
    170     LibraryView* wrap = lib_list->FindLibraryForAddress(address);
    171     if (wrap && wrap->IsCrazy()) {
    172       size_t sym_size = 0;
    173 
    174       SharedLibrary* lib = wrap->GetCrazy();
    175       ::memset(info, 0, sizeof(*info));
    176       info->dli_fname = lib->base_name();
    177       info->dli_fbase = reinterpret_cast<void*>(lib->load_address());
    178 
    179       // Determine if any symbol in the library contains the specified address.
    180       (void)lib->FindNearestSymbolForAddress(
    181           address, &info->dli_sname, &info->dli_saddr, &sym_size);
    182       return 0;
    183     }
    184   }
    185   // Otherwise, use system version.
    186   ::dlerror();
    187   int ret = ::dladdr(address, info);
    188   if (ret != 0)
    189     SaveSystemError();
    190   return ret;
    191 }
    192 
    193 int WrapDlclose(void* lib_handle) {
    194   LibraryView* wrap_lib = reinterpret_cast<LibraryView*>(lib_handle);
    195   if (!wrap_lib) {
    196     SetLinkerError("NULL library handle");
    197     return -1;
    198   }
    199 
    200   if (wrap_lib->IsSystem() || wrap_lib->IsCrazy()) {
    201     ScopedGlobalLock lock;
    202     LibraryList* lib_list = Globals::GetLibraries();
    203     lib_list->UnloadLibrary(wrap_lib);
    204     return 0;
    205   }
    206 
    207   // Invalid library handle!!
    208   SetLinkerError("Invalid library handle %p", lib_handle);
    209   return -1;
    210 }
    211 
    212 #ifdef __arm__
    213 _Unwind_Ptr WrapDl_unwind_find_exidx(_Unwind_Ptr pc, int* pcount) {
    214   // First lookup in crazy libraries.
    215   {
    216     ScopedGlobalLock lock;
    217     LibraryList* list = Globals::GetLibraries();
    218     _Unwind_Ptr result = list->FindArmExIdx(pc, pcount);
    219     if (result)
    220       return result;
    221   }
    222   // Lookup in system libraries.
    223   return ::dl_unwind_find_exidx(pc, pcount);
    224 }
    225 #else  // !__arm__
    226 int WrapDl_iterate_phdr(int (*cb)(dl_phdr_info*, size_t, void*), void* data) {
    227   // First, iterate over crazy libraries.
    228   {
    229     ScopedGlobalLock lock;
    230     LibraryList* list = Globals::GetLibraries();
    231     int result = list->IteratePhdr(cb, data);
    232     if (result)
    233       return result;
    234   }
    235   // Then lookup through system ones.
    236   return ::dl_iterate_phdr(cb, data);
    237 }
    238 #endif  // !__arm__
    239 
    240 }  // namespace
    241 
    242 void* WrapLinkerSymbol(const char* name) {
    243   // Shortcut, since all names begin with 'dl'
    244   // Take care of __aeabi_atexit on ARM though.
    245   if (name[0] != 'd' || name[1] != 'l') {
    246 #ifdef __arm__
    247     if (name[0] == '_' && !strcmp("__aeabi_atexit", name))
    248       return reinterpret_cast<void*>(&__aeabi_atexit);
    249 #endif
    250     return NULL;
    251   }
    252 
    253   static const struct {
    254     const char* name;
    255     void* address;
    256   } kSymbols[] = {
    257         {"dlopen", reinterpret_cast<void*>(&WrapDlopen)},
    258         {"dlclose", reinterpret_cast<void*>(&WrapDlclose)},
    259         {"dlerror", reinterpret_cast<void*>(&WrapDlerror)},
    260         {"dlsym", reinterpret_cast<void*>(&WrapDlsym)},
    261         {"dladdr", reinterpret_cast<void*>(&WrapDladdr)},
    262 #ifdef __arm__
    263         {"dl_unwind_find_exidx",
    264          reinterpret_cast<void*>(&WrapDl_unwind_find_exidx)},
    265 #else
    266         {"dl_iterate_phdr", reinterpret_cast<void*>(&WrapDl_iterate_phdr)},
    267 #endif
    268     };
    269   static const size_t kCount = sizeof(kSymbols) / sizeof(kSymbols[0]);
    270   for (size_t n = 0; n < kCount; ++n) {
    271     if (!strcmp(kSymbols[n].name, name))
    272       return kSymbols[n].address;
    273   }
    274   return NULL;
    275 }
    276 
    277 }  // namespace crazy
    278