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