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// 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
22using crazy::Globals;
23using crazy::Error;
24using crazy::SearchPathList;
25using crazy::ScopedGlobalLock;
26using crazy::LibraryView;
27
28//
29// crazy_context_t
30//
31
32struct crazy_context_t {
33 public:
34  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
58void crazy_context_t::ResetSearchPaths() {
59  search_paths.ResetFromEnv("LD_LIBRARY_PATH");
60}
61
62//
63// API functions
64//
65
66extern "C" {
67
68crazy_context_t* crazy_context_create(void) { return new crazy_context_t(); }
69
70const 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.
76void crazy_context_clear_error(crazy_context_t* context) {
77  context->error = "";
78}
79
80void crazy_context_set_load_address(crazy_context_t* context,
81                                    size_t load_address) {
82  context->load_address = load_address;
83}
84
85size_t crazy_context_get_load_address(crazy_context_t* context) {
86  return context->load_address;
87}
88
89void crazy_context_set_file_offset(crazy_context_t* context,
90                                   size_t file_offset) {
91  context->file_offset = file_offset;
92}
93
94size_t crazy_context_get_file_offset(crazy_context_t* context) {
95  return context->file_offset;
96}
97
98crazy_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
104crazy_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
122void crazy_context_reset_search_paths(crazy_context_t* context) {
123  context->ResetSearchPaths();
124}
125
126void 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
133void 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
140void 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
147void 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
154void crazy_callback_run(crazy_callback_t* callback) {
155  (*callback->handler)(callback->opaque);
156}
157
158void 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.
162class ScopedDelayedCallbackPoster {
163 public:
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
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.
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
196crazy_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
226crazy_status_t crazy_library_open_in_zip_file(crazy_library_t** library,
227                                              const char* zipfile_name,
228                                              const char* lib_name,
229                                              crazy_context_t* context) {
230  ScopedDelayedCallbackPoster poster(context);
231  ScopedGlobalLock lock;
232
233  LibraryView* wrap =
234      crazy::Globals::GetLibraries()->LoadLibraryInZipFile(
235          zipfile_name,
236          lib_name,
237          RTLD_NOW,
238          context->load_address,
239          &context->search_paths,
240          &context->error);
241
242  if (!wrap)
243    return CRAZY_STATUS_FAILURE;
244
245  if (context->java_vm != NULL && wrap->IsCrazy()) {
246    crazy::SharedLibrary* lib = wrap->GetCrazy();
247    if (!lib->SetJavaVM(
248             context->java_vm, context->minimum_jni_version, &context->error)) {
249      crazy::Globals::GetLibraries()->UnloadLibrary(wrap);
250      return CRAZY_STATUS_FAILURE;
251    }
252  }
253
254  *library = reinterpret_cast<crazy_library_t*>(wrap);
255  return CRAZY_STATUS_SUCCESS;
256}
257
258crazy_status_t crazy_library_get_info(crazy_library_t* library,
259                                      crazy_context_t* context,
260                                      crazy_library_info_t* info) {
261  if (!library) {
262    context->error = "Invalid library file handle";
263    return CRAZY_STATUS_FAILURE;
264  }
265
266  LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
267  if (!wrap->GetInfo(&info->load_address,
268                     &info->load_size,
269                     &info->relro_start,
270                     &info->relro_size,
271                     &context->error)) {
272    return CRAZY_STATUS_FAILURE;
273  }
274
275  return CRAZY_STATUS_SUCCESS;
276}
277
278crazy_status_t crazy_system_can_share_relro(void) {
279  crazy::AshmemRegion region;
280  if (!region.Allocate(PAGE_SIZE, NULL) ||
281      !region.SetProtectionFlags(PROT_READ) ||
282      !crazy::AshmemRegion::CheckFileDescriptorIsReadOnly(region.fd()))
283    return CRAZY_STATUS_FAILURE;
284
285  return CRAZY_STATUS_SUCCESS;
286}
287
288crazy_status_t crazy_library_create_shared_relro(crazy_library_t* library,
289                                                 crazy_context_t* context,
290                                                 size_t load_address,
291                                                 size_t* relro_start,
292                                                 size_t* relro_size,
293                                                 int* relro_fd) {
294  LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
295
296  if (!library || !wrap->IsCrazy()) {
297    context->error = "Invalid library file handle";
298    return CRAZY_STATUS_FAILURE;
299  }
300
301  crazy::SharedLibrary* lib = wrap->GetCrazy();
302  if (!lib->CreateSharedRelro(
303           load_address, relro_start, relro_size, relro_fd, &context->error))
304    return CRAZY_STATUS_FAILURE;
305
306  return CRAZY_STATUS_SUCCESS;
307}
308
309crazy_status_t crazy_library_use_shared_relro(crazy_library_t* library,
310                                              crazy_context_t* context,
311                                              size_t relro_start,
312                                              size_t relro_size,
313                                              int relro_fd) {
314  LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
315
316  if (!library || !wrap->IsCrazy()) {
317    context->error = "Invalid library file handle";
318    return CRAZY_STATUS_FAILURE;
319  }
320
321  crazy::SharedLibrary* lib = wrap->GetCrazy();
322  if (!lib->UseSharedRelro(relro_start, relro_size, relro_fd, &context->error))
323    return CRAZY_STATUS_FAILURE;
324
325  return CRAZY_STATUS_SUCCESS;
326}
327
328crazy_status_t crazy_library_find_by_name(const char* library_name,
329                                          crazy_library_t** library) {
330  {
331    ScopedGlobalLock lock;
332    LibraryView* wrap =
333        Globals::GetLibraries()->FindLibraryByName(library_name);
334    if (!wrap)
335      return CRAZY_STATUS_FAILURE;
336
337    wrap->AddRef();
338    *library = reinterpret_cast<crazy_library_t*>(wrap);
339  }
340  return CRAZY_STATUS_SUCCESS;
341}
342
343crazy_status_t crazy_library_find_symbol(crazy_library_t* library,
344                                         const char* symbol_name,
345                                         void** symbol_address) {
346  LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
347
348  // TODO(digit): Handle NULL symbols properly.
349  *symbol_address = wrap->LookupSymbol(symbol_name);
350  return (*symbol_address == NULL) ? CRAZY_STATUS_FAILURE
351                                   : CRAZY_STATUS_SUCCESS;
352}
353
354crazy_status_t crazy_linker_find_symbol(const char* symbol_name,
355                                        void** symbol_address) {
356  // TODO(digit): Implement this.
357  return CRAZY_STATUS_FAILURE;
358}
359
360crazy_status_t crazy_library_find_from_address(void* address,
361                                               crazy_library_t** library) {
362  {
363    ScopedGlobalLock lock;
364    LibraryView* wrap = Globals::GetLibraries()->FindLibraryForAddress(address);
365    if (!wrap)
366      return CRAZY_STATUS_FAILURE;
367
368    wrap->AddRef();
369
370    *library = reinterpret_cast<crazy_library_t*>(wrap);
371    return CRAZY_STATUS_SUCCESS;
372  }
373}
374
375void crazy_library_close(crazy_library_t* library) {
376  crazy_library_close_with_context(library, NULL);
377}
378
379void crazy_library_close_with_context(crazy_library_t* library,
380                                      crazy_context_t* context) {
381  if (library) {
382    ScopedDelayedCallbackPoster poster(context);
383    ScopedGlobalLock lock;
384    LibraryView* wrap = reinterpret_cast<LibraryView*>(library);
385
386    Globals::GetLibraries()->UnloadLibrary(wrap);
387  }
388}
389
390}  // extern "C"
391