1// Copyright 2014 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|
24extern "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.
31extern "C" int dl_iterate_phdr(int (*cb)(dl_phdr_info* info,
32                                         size_t size,
33                                         void* data),
34                               void* data);
35#endif
36
37namespace crazy {
38
39namespace {
40
41#ifdef __arm__
42extern "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.
50int __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.
56void SaveSystemError() {
57  ThreadData* data = GetThreadData();
58  data->SetError(::dlerror());
59}
60
61char* 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
70void* 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
103void* 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
165int 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
193int 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__
226int 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
242void* 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