• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
__aeabi_atexit(void * object,void (* destructor)(void *),void * dso_handle)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.
SaveSystemError()56 void SaveSystemError() {
57   ThreadData* data = GetThreadData();
58   data->SetError(::dlerror());
59 }
60 
WrapDlerror()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 
WrapDlopen(const char * path,int mode)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 
WrapDlsym(void * lib_handle,const char * symbol_name)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 
WrapDladdr(void * address,Dl_info * info)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 
WrapDlclose(void * lib_handle)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__
WrapDl_unwind_find_exidx(_Unwind_Ptr pc,int * pcount)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__
WrapDl_iterate_phdr(int (* cb)(dl_phdr_info *,size_t,void *),void * data)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 
WrapLinkerSymbol(const char * name)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