Home | History | Annotate | Download | only in src
      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 
     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:
     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 
     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 
     68 crazy_context_t* crazy_context_create(void) { return new crazy_context_t(); }
     69 
     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.
     76 void crazy_context_clear_error(crazy_context_t* context) {
     77   context->error = "";
     78 }
     79 
     80 void crazy_context_set_load_address(crazy_context_t* context,
     81                                     size_t load_address) {
     82   context->load_address = load_address;
     83 }
     84 
     85 size_t crazy_context_get_load_address(crazy_context_t* context) {
     86   return context->load_address;
     87 }
     88 
     89 void crazy_context_set_file_offset(crazy_context_t* context,
     90                                    size_t file_offset) {
     91   context->file_offset = file_offset;
     92 }
     93 
     94 size_t crazy_context_get_file_offset(crazy_context_t* context) {
     95   return context->file_offset;
     96 }
     97 
     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 
    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 
    122 void crazy_context_reset_search_paths(crazy_context_t* context) {
    123   context->ResetSearchPaths();
    124 }
    125 
    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 
    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 
    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 
    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 
    154 void crazy_callback_run(crazy_callback_t* callback) {
    155   (*callback->handler)(callback->opaque);
    156 }
    157 
    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:
    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 
    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 
    226 crazy_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 
    258 crazy_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 
    278 crazy_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 
    288 crazy_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 
    309 crazy_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 
    328 crazy_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 
    343 crazy_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 
    354 crazy_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 
    360 crazy_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 
    375 void crazy_library_close(crazy_library_t* library) {
    376   crazy_library_close_with_context(library, NULL);
    377 }
    378 
    379 void 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