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