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