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 // Implements the crazy linker C-based API exposed by <crazy_linker.h>
6
7 #include <crazy_linker.h>
8
9 #include <string.h>
10
11 #include "crazy_linker_error.h"
12 #include "crazy_linker_ashmem.h"
13 #include "crazy_linker_globals.h"
14 #include "crazy_linker_proc_maps.h"
15 #include "crazy_linker_search_path_list.h"
16 #include "crazy_linker_shared_library.h"
17 #include "crazy_linker_thread.h"
18 #include "crazy_linker_util.h"
19 #include "crazy_linker_library_view.h"
20 #include "crazy_linker_system.h"
21
22 using crazy::Globals;
23 using crazy::Error;
24 using crazy::SearchPathList;
25 using crazy::ScopedGlobalLock;
26 using crazy::LibraryView;
27
28 //
29 // crazy_context_t
30 //
31
32 struct crazy_context_t {
33 public:
crazy_context_tcrazy_context_t34 crazy_context_t()
35 : load_address(0),
36 file_offset(0),
37 error(),
38 search_paths(),
39 java_vm(NULL),
40 minimum_jni_version(0),
41 callback_poster(NULL),
42 callback_poster_opaque(NULL) {
43 ResetSearchPaths();
44 }
45
46 void ResetSearchPaths();
47
48 size_t load_address;
49 size_t file_offset;
50 Error error;
51 SearchPathList search_paths;
52 void* java_vm;
53 int minimum_jni_version;
54 crazy_callback_poster_t callback_poster;
55 void* callback_poster_opaque;
56 };
57
ResetSearchPaths()58 void crazy_context_t::ResetSearchPaths() {
59 search_paths.ResetFromEnv("LD_LIBRARY_PATH");
60 }
61
62 //
63 // API functions
64 //
65
66 extern "C" {
67
crazy_context_create(void)68 crazy_context_t* crazy_context_create(void) { return new crazy_context_t(); }
69
crazy_context_get_error(crazy_context_t * context)70 const char* crazy_context_get_error(crazy_context_t* context) {
71 const char* error = context->error.c_str();
72 return (error[0] != '\0') ? error : NULL;
73 }
74
75 // Clear error in a given context.
crazy_context_clear_error(crazy_context_t * context)76 void crazy_context_clear_error(crazy_context_t* context) {
77 context->error = "";
78 }
79
crazy_context_set_load_address(crazy_context_t * context,size_t load_address)80 void crazy_context_set_load_address(crazy_context_t* context,
81 size_t load_address) {
82 context->load_address = load_address;
83 }
84
crazy_context_get_load_address(crazy_context_t * context)85 size_t crazy_context_get_load_address(crazy_context_t* context) {
86 return context->load_address;
87 }
88
crazy_context_set_file_offset(crazy_context_t * context,size_t file_offset)89 void crazy_context_set_file_offset(crazy_context_t* context,
90 size_t file_offset) {
91 context->file_offset = file_offset;
92 }
93
crazy_context_get_file_offset(crazy_context_t * context)94 size_t crazy_context_get_file_offset(crazy_context_t* context) {
95 return context->file_offset;
96 }
97
crazy_context_add_search_path(crazy_context_t * context,const char * file_path)98 crazy_status_t crazy_context_add_search_path(crazy_context_t* context,
99 const char* file_path) {
100 context->search_paths.AddPaths(file_path);
101 return CRAZY_STATUS_SUCCESS;
102 }
103
crazy_context_add_search_path_for_address(crazy_context_t * context,void * address)104 crazy_status_t crazy_context_add_search_path_for_address(
105 crazy_context_t* context,
106 void* address) {
107 uintptr_t load_address;
108 char path[512];
109 char* p;
110
111 if (crazy::FindElfBinaryForAddress(
112 address, &load_address, path, sizeof(path)) &&
113 (p = strrchr(path, '/')) != NULL && p[1]) {
114 *p = '\0';
115 return crazy_context_add_search_path(context, path);
116 }
117
118 context->error.Format("Could not find ELF binary at address @%p", address);
119 return CRAZY_STATUS_FAILURE;
120 }
121
crazy_context_reset_search_paths(crazy_context_t * context)122 void crazy_context_reset_search_paths(crazy_context_t* context) {
123 context->ResetSearchPaths();
124 }
125
crazy_context_set_java_vm(crazy_context_t * context,void * java_vm,int minimum_jni_version)126 void crazy_context_set_java_vm(crazy_context_t* context,
127 void* java_vm,
128 int minimum_jni_version) {
129 context->java_vm = java_vm;
130 context->minimum_jni_version = minimum_jni_version;
131 }
132
crazy_context_get_java_vm(crazy_context_t * context,void ** java_vm,int * minimum_jni_version)133 void crazy_context_get_java_vm(crazy_context_t* context,
134 void** java_vm,
135 int* minimum_jni_version) {
136 *java_vm = context->java_vm;
137 *minimum_jni_version = context->minimum_jni_version;
138 }
139
crazy_context_set_callback_poster(crazy_context_t * context,crazy_callback_poster_t poster,void * poster_opaque)140 void crazy_context_set_callback_poster(crazy_context_t* context,
141 crazy_callback_poster_t poster,
142 void* poster_opaque) {
143 context->callback_poster = poster;
144 context->callback_poster_opaque = poster_opaque;
145 }
146
crazy_context_get_callback_poster(crazy_context_t * context,crazy_callback_poster_t * poster,void ** poster_opaque)147 void crazy_context_get_callback_poster(crazy_context_t* context,
148 crazy_callback_poster_t* poster,
149 void** poster_opaque) {
150 *poster = context->callback_poster;
151 *poster_opaque = context->callback_poster_opaque;
152 }
153
crazy_callback_run(crazy_callback_t * callback)154 void crazy_callback_run(crazy_callback_t* callback) {
155 (*callback->handler)(callback->opaque);
156 }
157
crazy_context_destroy(crazy_context_t * context)158 void crazy_context_destroy(crazy_context_t* context) { delete context; }
159
160 // Scoped delayed execution, removes RDebug callbacks on scope exit. No-op
161 // if callback is NULL.
162 class ScopedDelayedCallbackPoster {
163 public:
ScopedDelayedCallbackPoster(crazy_context_t * context)164 ScopedDelayedCallbackPoster(crazy_context_t* context) {
165 if (context && context->callback_poster) {
166 crazy::Globals::GetRDebug()->SetDelayedCallbackPoster(&PostFromContext,
167 context);
168 set_delayed_callback_poster_ = true;
169 } else {
170 set_delayed_callback_poster_ = false;
171 }
172 }
173
~ScopedDelayedCallbackPoster()174 ~ScopedDelayedCallbackPoster() {
175 if (set_delayed_callback_poster_)
176 crazy::Globals::GetRDebug()->SetDelayedCallbackPoster(NULL, NULL);
177 }
178
179 private:
180 // Wrap callback hander and opaque into a call to a crazy_context_poster_t.
PostFromContext(void * crazy_context,crazy_callback_handler_t handler,void * opaque)181 static bool PostFromContext(void* crazy_context,
182 crazy_callback_handler_t handler,
183 void* opaque) {
184 crazy_context_t* context = static_cast<crazy_context_t*>(crazy_context);
185 crazy_callback_t callback;
186 callback.handler = handler;
187 callback.opaque = opaque;
188 return context->callback_poster(&callback,
189 context->callback_poster_opaque);
190 }
191
192 // True if the context offered a callback_poster, otherwise false.
193 bool set_delayed_callback_poster_;
194 };
195
crazy_library_open(crazy_library_t ** library,const char * lib_name,crazy_context_t * context)196 crazy_status_t crazy_library_open(crazy_library_t** library,
197 const char* lib_name,
198 crazy_context_t* context) {
199 ScopedDelayedCallbackPoster poster(context);
200 ScopedGlobalLock lock;
201
202 LibraryView* wrap =
203 crazy::Globals::GetLibraries()->LoadLibrary(lib_name,
204 RTLD_NOW,
205 context->load_address,
206 context->file_offset,
207 &context->search_paths,
208 &context->error);
209
210 if (!wrap)
211 return CRAZY_STATUS_FAILURE;
212
213 if (context->java_vm != NULL && wrap->IsCrazy()) {
214 crazy::SharedLibrary* lib = wrap->GetCrazy();
215 if (!lib->SetJavaVM(
216 context->java_vm, context->minimum_jni_version, &context->error)) {
217 crazy::Globals::GetLibraries()->UnloadLibrary(wrap);
218 return CRAZY_STATUS_FAILURE;
219 }
220 }
221
222 *library = reinterpret_cast<crazy_library_t*>(wrap);
223 return CRAZY_STATUS_SUCCESS;
224 }
225
crazy_library_get_info(crazy_library_t * library,crazy_context_t * context,crazy_library_info_t * info)226 crazy_status_t crazy_library_get_info(crazy_library_t* library,
227 crazy_context_t* context,
228 crazy_library_info_t* info) {
229 if (!library) {
230 context->error = "Invalid library file handle";
231 return CRAZY_STATUS_FAILURE;
232 }
233
234 LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
235 if (!wrap->GetInfo(&info->load_address,
236 &info->load_size,
237 &info->relro_start,
238 &info->relro_size,
239 &context->error)) {
240 return CRAZY_STATUS_FAILURE;
241 }
242
243 return CRAZY_STATUS_SUCCESS;
244 }
245
crazy_system_can_share_relro(void)246 crazy_status_t crazy_system_can_share_relro(void) {
247 crazy::AshmemRegion region;
248 if (!region.Allocate(PAGE_SIZE, NULL) ||
249 !region.SetProtectionFlags(PROT_READ) ||
250 !crazy::AshmemRegion::CheckFileDescriptorIsReadOnly(region.fd()))
251 return CRAZY_STATUS_FAILURE;
252
253 return CRAZY_STATUS_SUCCESS;
254 }
255
crazy_library_create_shared_relro(crazy_library_t * library,crazy_context_t * context,size_t load_address,size_t * relro_start,size_t * relro_size,int * relro_fd)256 crazy_status_t crazy_library_create_shared_relro(crazy_library_t* library,
257 crazy_context_t* context,
258 size_t load_address,
259 size_t* relro_start,
260 size_t* relro_size,
261 int* relro_fd) {
262 LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
263
264 if (!library || !wrap->IsCrazy()) {
265 context->error = "Invalid library file handle";
266 return CRAZY_STATUS_FAILURE;
267 }
268
269 crazy::SharedLibrary* lib = wrap->GetCrazy();
270 if (!lib->CreateSharedRelro(
271 load_address, relro_start, relro_size, relro_fd, &context->error))
272 return CRAZY_STATUS_FAILURE;
273
274 return CRAZY_STATUS_SUCCESS;
275 }
276
crazy_library_use_shared_relro(crazy_library_t * library,crazy_context_t * context,size_t relro_start,size_t relro_size,int relro_fd)277 crazy_status_t crazy_library_use_shared_relro(crazy_library_t* library,
278 crazy_context_t* context,
279 size_t relro_start,
280 size_t relro_size,
281 int relro_fd) {
282 LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
283
284 if (!library || !wrap->IsCrazy()) {
285 context->error = "Invalid library file handle";
286 return CRAZY_STATUS_FAILURE;
287 }
288
289 crazy::SharedLibrary* lib = wrap->GetCrazy();
290 if (!lib->UseSharedRelro(relro_start, relro_size, relro_fd, &context->error))
291 return CRAZY_STATUS_FAILURE;
292
293 return CRAZY_STATUS_SUCCESS;
294 }
295
crazy_library_find_by_name(const char * library_name,crazy_library_t ** library)296 crazy_status_t crazy_library_find_by_name(const char* library_name,
297 crazy_library_t** library) {
298 {
299 ScopedGlobalLock lock;
300 LibraryView* wrap =
301 Globals::GetLibraries()->FindLibraryByName(library_name);
302 if (!wrap)
303 return CRAZY_STATUS_FAILURE;
304
305 wrap->AddRef();
306 *library = reinterpret_cast<crazy_library_t*>(wrap);
307 }
308 return CRAZY_STATUS_SUCCESS;
309 }
310
crazy_library_find_symbol(crazy_library_t * library,const char * symbol_name,void ** symbol_address)311 crazy_status_t crazy_library_find_symbol(crazy_library_t* library,
312 const char* symbol_name,
313 void** symbol_address) {
314 LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
315
316 // TODO(digit): Handle NULL symbols properly.
317 *symbol_address = wrap->LookupSymbol(symbol_name);
318 return (*symbol_address == NULL) ? CRAZY_STATUS_FAILURE
319 : CRAZY_STATUS_SUCCESS;
320 }
321
crazy_linker_find_symbol(const char * symbol_name,void ** symbol_address)322 crazy_status_t crazy_linker_find_symbol(const char* symbol_name,
323 void** symbol_address) {
324 // TODO(digit): Implement this.
325 return CRAZY_STATUS_FAILURE;
326 }
327
crazy_library_find_from_address(void * address,crazy_library_t ** library)328 crazy_status_t crazy_library_find_from_address(void* address,
329 crazy_library_t** library) {
330 {
331 ScopedGlobalLock lock;
332 LibraryView* wrap = Globals::GetLibraries()->FindLibraryForAddress(address);
333 if (!wrap)
334 return CRAZY_STATUS_FAILURE;
335
336 wrap->AddRef();
337
338 *library = reinterpret_cast<crazy_library_t*>(wrap);
339 return CRAZY_STATUS_SUCCESS;
340 }
341 }
342
crazy_library_close(crazy_library_t * library)343 void crazy_library_close(crazy_library_t* library) {
344 crazy_library_close_with_context(library, NULL);
345 }
346
crazy_library_close_with_context(crazy_library_t * library,crazy_context_t * context)347 void crazy_library_close_with_context(crazy_library_t* library,
348 crazy_context_t* context) {
349 if (library) {
350 ScopedDelayedCallbackPoster poster(context);
351 ScopedGlobalLock lock;
352 LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
353
354 Globals::GetLibraries()->UnloadLibrary(wrap);
355 }
356 }
357
358 } // extern "C"
359