Home | History | Annotate | Download | only in src
      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:
     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_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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    343 void crazy_library_close(crazy_library_t* library) {
    344   crazy_library_close_with_context(library, NULL);
    345 }
    346 
    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