Home | History | Annotate | Download | only in linker
      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 // This is the Android-specific Chromium linker, a tiny shared library
      6 // implementing a custom dynamic linker that can be used to load the
      7 // real Chromium libraries (e.g. libcontentshell.so).
      8 
      9 // The main point of this linker is to be able to share the RELRO
     10 // section of libcontentshell.so (or equivalent) between the browser and
     11 // renderer process.
     12 
     13 // This source code *cannot* depend on anything from base/ or the C++
     14 // STL, to keep the final library small, and avoid ugly dependency issues.
     15 
     16 #include <android/log.h>
     17 #include <crazy_linker.h>
     18 #include <jni.h>
     19 #include <stdlib.h>
     20 #include <sys/mman.h>
     21 #include <unistd.h>
     22 
     23 // Set this to 1 to enable debug traces to the Android log.
     24 // Note that LOG() from "base/logging.h" cannot be used, since it is
     25 // in base/ which hasn't been loaded yet.
     26 #define DEBUG 0
     27 
     28 #define TAG "chromium_android_linker"
     29 
     30 #if DEBUG
     31 #define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
     32 #else
     33 #define LOG_INFO(...) ((void)0)
     34 #endif
     35 #define LOG_ERROR(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
     36 
     37 #define UNUSED __attribute__((unused))
     38 
     39 namespace {
     40 
     41 // A simply scoped UTF String class that can be initialized from
     42 // a Java jstring handle. Modeled like std::string, which cannot
     43 // be used here.
     44 class String {
     45  public:
     46   String(JNIEnv* env, jstring str);
     47 
     48   ~String() {
     49     if (ptr_)
     50       ::free(ptr_);
     51   }
     52 
     53   const char* c_str() const { return ptr_ ? ptr_ : ""; }
     54   size_t size() const { return size_; }
     55 
     56  private:
     57   char* ptr_;
     58   size_t size_;
     59 };
     60 
     61 String::String(JNIEnv* env, jstring str) {
     62   size_ = env->GetStringUTFLength(str);
     63   ptr_ = static_cast<char*>(::malloc(size_ + 1));
     64 
     65   // Note: the result contains Java "modified UTF-8" bytes.
     66   // Good enough for the linker though.
     67   const char* bytes = env->GetStringUTFChars(str, NULL);
     68   ::memcpy(ptr_, bytes, size_);
     69   ptr_[size_] = '\0';
     70 
     71   env->ReleaseStringUTFChars(str, bytes);
     72 }
     73 
     74 // Return true iff |address| is a valid address for the target CPU.
     75 bool IsValidAddress(jlong address) {
     76   return static_cast<jlong>(static_cast<size_t>(address)) == address;
     77 }
     78 
     79 // Find the jclass JNI reference corresponding to a given |class_name|.
     80 // |env| is the current JNI environment handle.
     81 // On success, return true and set |*clazz|.
     82 bool InitClassReference(JNIEnv* env, const char* class_name, jclass* clazz) {
     83   *clazz = env->FindClass(class_name);
     84   if (!*clazz) {
     85     LOG_ERROR("Could not find class for %s", class_name);
     86     return false;
     87   }
     88   return true;
     89 }
     90 
     91 // Initialize a jfieldID corresponding to the field of a given |clazz|,
     92 // with name |field_name| and signature |field_sig|.
     93 // |env| is the current JNI environment handle.
     94 // On success, return true and set |*field_id|.
     95 bool InitFieldId(JNIEnv* env,
     96                  jclass clazz,
     97                  const char* field_name,
     98                  const char* field_sig,
     99                  jfieldID* field_id) {
    100   *field_id = env->GetFieldID(clazz, field_name, field_sig);
    101   if (!*field_id) {
    102     LOG_ERROR("Could not find ID for field '%s'", field_name);
    103     return false;
    104   }
    105   LOG_INFO(
    106       "%s: Found ID %p for field '%s'", __FUNCTION__, *field_id, field_name);
    107   return true;
    108 }
    109 
    110 // Initialize a jmethodID corresponding to the static method of a given
    111 // |clazz|, with name |method_name| and signature |method_sig|.
    112 // |env| is the current JNI environment handle.
    113 // On success, return true and set |*method_id|.
    114 bool InitStaticMethodId(JNIEnv* env,
    115                         jclass clazz,
    116                         const char* method_name,
    117                         const char* method_sig,
    118                         jmethodID* method_id) {
    119   *method_id = env->GetStaticMethodID(clazz, method_name, method_sig);
    120   if (!*method_id) {
    121     LOG_ERROR("Could not find ID for static method '%s'", method_name);
    122     return false;
    123   }
    124   LOG_INFO("%s: Found ID %p for static method '%s'",
    125            __FUNCTION__, *method_id, method_name);
    126   return true;
    127 }
    128 
    129 // A class used to model the field IDs of the org.chromium.base.Linker
    130 // LibInfo inner class, used to communicate data with the Java side
    131 // of the linker.
    132 struct LibInfo_class {
    133   jfieldID load_address_id;
    134   jfieldID load_size_id;
    135   jfieldID relro_start_id;
    136   jfieldID relro_size_id;
    137   jfieldID relro_fd_id;
    138 
    139   // Initialize an instance.
    140   bool Init(JNIEnv* env) {
    141     jclass clazz;
    142     if (!InitClassReference(
    143              env, "org/chromium/base/library_loader/Linker$LibInfo", &clazz)) {
    144       return false;
    145     }
    146 
    147     return InitFieldId(env, clazz, "mLoadAddress", "J", &load_address_id) &&
    148            InitFieldId(env, clazz, "mLoadSize", "J", &load_size_id) &&
    149            InitFieldId(env, clazz, "mRelroStart", "J", &relro_start_id) &&
    150            InitFieldId(env, clazz, "mRelroSize", "J", &relro_size_id) &&
    151            InitFieldId(env, clazz, "mRelroFd", "I", &relro_fd_id);
    152   }
    153 
    154   void SetLoadInfo(JNIEnv* env,
    155                    jobject library_info_obj,
    156                    size_t load_address,
    157                    size_t load_size) {
    158     env->SetLongField(library_info_obj, load_address_id, load_address);
    159     env->SetLongField(library_info_obj, load_size_id, load_size);
    160   }
    161 
    162   // Use this instance to convert a RelroInfo reference into
    163   // a crazy_library_info_t.
    164   void GetRelroInfo(JNIEnv* env,
    165                     jobject library_info_obj,
    166                     size_t* relro_start,
    167                     size_t* relro_size,
    168                     int* relro_fd) {
    169     *relro_start = static_cast<size_t>(
    170         env->GetLongField(library_info_obj, relro_start_id));
    171 
    172     *relro_size =
    173         static_cast<size_t>(env->GetLongField(library_info_obj, relro_size_id));
    174 
    175     *relro_fd = env->GetIntField(library_info_obj, relro_fd_id);
    176   }
    177 
    178   void SetRelroInfo(JNIEnv* env,
    179                     jobject library_info_obj,
    180                     size_t relro_start,
    181                     size_t relro_size,
    182                     int relro_fd) {
    183     env->SetLongField(library_info_obj, relro_start_id, relro_start);
    184     env->SetLongField(library_info_obj, relro_size_id, relro_size);
    185     env->SetIntField(library_info_obj, relro_fd_id, relro_fd);
    186   }
    187 };
    188 
    189 static LibInfo_class s_lib_info_fields;
    190 
    191 // The linker uses a single crazy_context_t object created on demand.
    192 // There is no need to protect this against concurrent access, locking
    193 // is already handled on the Java side.
    194 static crazy_context_t* s_crazy_context;
    195 
    196 crazy_context_t* GetCrazyContext() {
    197   if (!s_crazy_context) {
    198     // Create new context.
    199     s_crazy_context = crazy_context_create();
    200 
    201     // Ensure libraries located in the same directory as the linker
    202     // can be loaded before system ones.
    203     crazy_context_add_search_path_for_address(
    204         s_crazy_context, reinterpret_cast<void*>(&s_crazy_context));
    205   }
    206 
    207   return s_crazy_context;
    208 }
    209 
    210 // A scoped crazy_library_t that automatically closes the handle
    211 // on scope exit, unless Release() has been called.
    212 class ScopedLibrary {
    213  public:
    214   ScopedLibrary() : lib_(NULL) {}
    215 
    216   ~ScopedLibrary() {
    217     if (lib_)
    218       crazy_library_close_with_context(lib_, GetCrazyContext());
    219   }
    220 
    221   crazy_library_t* Get() { return lib_; }
    222 
    223   crazy_library_t** GetPtr() { return &lib_; }
    224 
    225   crazy_library_t* Release() {
    226     crazy_library_t* ret = lib_;
    227     lib_ = NULL;
    228     return ret;
    229   }
    230 
    231  private:
    232   crazy_library_t* lib_;
    233 };
    234 
    235 namespace {
    236 
    237 template <class LibraryOpener>
    238 bool GenericLoadLibrary(
    239     JNIEnv* env,
    240     const char* library_name, jlong load_address, jobject lib_info_obj,
    241     const LibraryOpener& opener) {
    242   crazy_context_t* context = GetCrazyContext();
    243 
    244   if (!IsValidAddress(load_address)) {
    245     LOG_ERROR("%s: Invalid address 0x%llx", __FUNCTION__, load_address);
    246     return false;
    247   }
    248 
    249   // Set the desired load address (0 means randomize it).
    250   crazy_context_set_load_address(context, static_cast<size_t>(load_address));
    251 
    252   ScopedLibrary library;
    253   if (!opener.Open(library.GetPtr(), library_name, context)) {
    254     return false;
    255   }
    256 
    257   crazy_library_info_t info;
    258   if (!crazy_library_get_info(library.Get(), context, &info)) {
    259     LOG_ERROR("%s: Could not get library information for %s: %s",
    260               __FUNCTION__,
    261               library_name,
    262               crazy_context_get_error(context));
    263     return false;
    264   }
    265 
    266   // Release library object to keep it alive after the function returns.
    267   library.Release();
    268 
    269   s_lib_info_fields.SetLoadInfo(
    270       env, lib_info_obj, info.load_address, info.load_size);
    271   LOG_INFO("%s: Success loading library %s", __FUNCTION__, library_name);
    272   return true;
    273 }
    274 
    275 // Used for opening the library in a regular file.
    276 class FileLibraryOpener {
    277  public:
    278   bool Open(
    279       crazy_library_t** library,
    280       const char* library_name,
    281       crazy_context_t* context) const;
    282 };
    283 
    284 bool FileLibraryOpener::Open(
    285     crazy_library_t** library,
    286     const char* library_name,
    287     crazy_context_t* context) const {
    288   if (!crazy_library_open(library, library_name, context)) {
    289     LOG_ERROR("%s: Could not open %s: %s",
    290               __FUNCTION__,
    291               library_name,
    292               crazy_context_get_error(context));
    293     return false;
    294   }
    295   return true;
    296 }
    297 
    298 // Used for opening the library in a zip file.
    299 class ZipLibraryOpener {
    300  public:
    301   explicit ZipLibraryOpener(const char* zip_file) : zip_file_(zip_file) {}
    302   bool Open(
    303       crazy_library_t** library,
    304       const char* library_name,
    305       crazy_context_t* context) const;
    306  private:
    307   const char* zip_file_;
    308 };
    309 
    310 bool ZipLibraryOpener::Open(
    311     crazy_library_t** library,
    312     const char* library_name,
    313     crazy_context_t* context) const {
    314   if (!crazy_library_open_in_zip_file(
    315           library, zip_file_, library_name, context)) {
    316      LOG_ERROR("%s: Could not open %s in zip file %s: %s",
    317                __FUNCTION__, library_name, zip_file_,
    318                crazy_context_get_error(context));
    319      return false;
    320   }
    321   return true;
    322 }
    323 
    324 }  // unnamed namespace
    325 
    326 // Load a library with the chromium linker. This will also call its
    327 // JNI_OnLoad() method, which shall register its methods. Note that
    328 // lazy native method resolution will _not_ work after this, because
    329 // Dalvik uses the system's dlsym() which won't see the new library,
    330 // so explicit registration is mandatory.
    331 // |env| is the current JNI environment handle.
    332 // |clazz| is the static class handle for org.chromium.base.Linker,
    333 // and is ignored here.
    334 // |library_name| is the library name (e.g. libfoo.so).
    335 // |load_address| is an explicit load address.
    336 // |library_info| is a LibInfo handle used to communicate information
    337 // with the Java side.
    338 // Return true on success.
    339 jboolean LoadLibrary(JNIEnv* env,
    340                      jclass clazz,
    341                      jstring library_name,
    342                      jlong load_address,
    343                      jobject lib_info_obj) {
    344   String lib_name(env, library_name);
    345   FileLibraryOpener opener;
    346   return GenericLoadLibrary(
    347       env, lib_name.c_str(),
    348       static_cast<size_t>(load_address), lib_info_obj, opener);
    349 }
    350 
    351 // Load a library from a zipfile with the chromium linker. The
    352 // library in the zipfile must be uncompressed and page aligned.
    353 // The basename of the library is given. The library is expected
    354 // to be lib/<abi_tag>/crazy.<basename>. The <abi_tag> used will be the
    355 // same as the abi for this linker. The "crazy." prefix is included
    356 // so that the Android Package Manager doesn't extract the library into
    357 // /data/app-lib.
    358 //
    359 // Loading the library will also call its JNI_OnLoad() method, which
    360 // shall register its methods. Note that lazy native method resolution
    361 // will _not_ work after this, because Dalvik uses the system's dlsym()
    362 // which won't see the new library, so explicit registration is mandatory.
    363 //
    364 // |env| is the current JNI environment handle.
    365 // |clazz| is the static class handle for org.chromium.base.Linker,
    366 // and is ignored here.
    367 // |zipfile_name| is the filename of the zipfile containing the library.
    368 // |library_name| is the library base name (e.g. libfoo.so).
    369 // |load_address| is an explicit load address.
    370 // |library_info| is a LibInfo handle used to communicate information
    371 // with the Java side.
    372 // Returns true on success.
    373 jboolean LoadLibraryInZipFile(JNIEnv* env,
    374                               jclass clazz,
    375                               jstring zipfile_name,
    376                               jstring library_name,
    377                               jlong load_address,
    378                               jobject lib_info_obj) {
    379   String zipfile_name_str(env, zipfile_name);
    380   String lib_name(env, library_name);
    381   ZipLibraryOpener opener(zipfile_name_str.c_str());
    382   return GenericLoadLibrary(
    383       env, lib_name.c_str(),
    384       static_cast<size_t>(load_address), lib_info_obj, opener);
    385 }
    386 
    387 // Class holding the Java class and method ID for the Java side Linker
    388 // postCallbackOnMainThread method.
    389 struct JavaCallbackBindings_class {
    390   jclass clazz;
    391   jmethodID method_id;
    392 
    393   // Initialize an instance.
    394   bool Init(JNIEnv* env, jclass linker_class) {
    395     clazz = reinterpret_cast<jclass>(env->NewGlobalRef(linker_class));
    396     return InitStaticMethodId(env,
    397                               linker_class,
    398                               "postCallbackOnMainThread",
    399                               "(J)V",
    400                               &method_id);
    401   }
    402 };
    403 
    404 static JavaCallbackBindings_class s_java_callback_bindings;
    405 
    406 // Designated receiver function for callbacks from Java. Its name is known
    407 // to the Java side.
    408 // |env| is the current JNI environment handle and is ignored here.
    409 // |clazz| is the static class handle for org.chromium.base.Linker,
    410 // and is ignored here.
    411 // |arg| is a pointer to an allocated crazy_callback_t, deleted after use.
    412 void RunCallbackOnUiThread(JNIEnv* env, jclass clazz, jlong arg) {
    413   crazy_callback_t* callback = reinterpret_cast<crazy_callback_t*>(arg);
    414 
    415   LOG_INFO("%s: Called back from java with handler %p, opaque %p",
    416            __FUNCTION__, callback->handler, callback->opaque);
    417 
    418   crazy_callback_run(callback);
    419   delete callback;
    420 }
    421 
    422 // Request a callback from Java. The supplied crazy_callback_t is valid only
    423 // for the duration of this call, so we copy it to a newly allocated
    424 // crazy_callback_t and then call the Java side's postCallbackOnMainThread.
    425 // This will call back to to our RunCallbackOnUiThread some time
    426 // later on the UI thread.
    427 // |callback_request| is a crazy_callback_t.
    428 // |poster_opaque| is unused.
    429 // Returns true if the callback request succeeds.
    430 static bool PostForLaterExecution(crazy_callback_t* callback_request,
    431                                   void* poster_opaque UNUSED) {
    432   crazy_context_t* context = GetCrazyContext();
    433 
    434   JavaVM* vm;
    435   int minimum_jni_version;
    436   crazy_context_get_java_vm(context,
    437                             reinterpret_cast<void**>(&vm),
    438                             &minimum_jni_version);
    439 
    440   // Do not reuse JNIEnv from JNI_OnLoad, but retrieve our own.
    441   JNIEnv* env;
    442   if (JNI_OK != vm->GetEnv(
    443       reinterpret_cast<void**>(&env), minimum_jni_version)) {
    444     LOG_ERROR("Could not create JNIEnv");
    445     return false;
    446   }
    447 
    448   // Copy the callback; the one passed as an argument may be temporary.
    449   crazy_callback_t* callback = new crazy_callback_t();
    450   *callback = *callback_request;
    451 
    452   LOG_INFO("%s: Calling back to java with handler %p, opaque %p",
    453            __FUNCTION__, callback->handler, callback->opaque);
    454 
    455   jlong arg = static_cast<jlong>(reinterpret_cast<uintptr_t>(callback));
    456 
    457   env->CallStaticVoidMethod(
    458       s_java_callback_bindings.clazz, s_java_callback_bindings.method_id, arg);
    459 
    460   // Back out and return false if we encounter a JNI exception.
    461   if (env->ExceptionCheck() == JNI_TRUE) {
    462     env->ExceptionDescribe();
    463     env->ExceptionClear();
    464     delete callback;
    465     return false;
    466   }
    467 
    468   return true;
    469 }
    470 
    471 jboolean CreateSharedRelro(JNIEnv* env,
    472                            jclass clazz,
    473                            jstring library_name,
    474                            jlong load_address,
    475                            jobject lib_info_obj) {
    476   String lib_name(env, library_name);
    477 
    478   LOG_INFO("%s: Called for %s", __FUNCTION__, lib_name.c_str());
    479 
    480   if (!IsValidAddress(load_address)) {
    481     LOG_ERROR("%s: Invalid address 0x%llx", __FUNCTION__, load_address);
    482     return false;
    483   }
    484 
    485   ScopedLibrary library;
    486   if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
    487     LOG_ERROR("%s: Could not find %s", __FUNCTION__, lib_name.c_str());
    488     return false;
    489   }
    490 
    491   crazy_context_t* context = GetCrazyContext();
    492   size_t relro_start = 0;
    493   size_t relro_size = 0;
    494   int relro_fd = -1;
    495 
    496   if (!crazy_library_create_shared_relro(library.Get(),
    497                                          context,
    498                                          static_cast<size_t>(load_address),
    499                                          &relro_start,
    500                                          &relro_size,
    501                                          &relro_fd)) {
    502     LOG_ERROR("%s: Could not create shared RELRO sharing for %s: %s\n",
    503               __FUNCTION__,
    504               lib_name.c_str(),
    505               crazy_context_get_error(context));
    506     return false;
    507   }
    508 
    509   s_lib_info_fields.SetRelroInfo(
    510       env, lib_info_obj, relro_start, relro_size, relro_fd);
    511   return true;
    512 }
    513 
    514 jboolean UseSharedRelro(JNIEnv* env,
    515                         jclass clazz,
    516                         jstring library_name,
    517                         jobject lib_info_obj) {
    518   String lib_name(env, library_name);
    519 
    520   LOG_INFO("%s: called for %s, lib_info_ref=%p",
    521            __FUNCTION__,
    522            lib_name.c_str(),
    523            lib_info_obj);
    524 
    525   ScopedLibrary library;
    526   if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
    527     LOG_ERROR("%s: Could not find %s", __FUNCTION__, lib_name.c_str());
    528     return false;
    529   }
    530 
    531   crazy_context_t* context = GetCrazyContext();
    532   size_t relro_start = 0;
    533   size_t relro_size = 0;
    534   int relro_fd = -1;
    535   s_lib_info_fields.GetRelroInfo(
    536       env, lib_info_obj, &relro_start, &relro_size, &relro_fd);
    537 
    538   LOG_INFO("%s: library=%s relro start=%p size=%p fd=%d",
    539            __FUNCTION__,
    540            lib_name.c_str(),
    541            (void*)relro_start,
    542            (void*)relro_size,
    543            relro_fd);
    544 
    545   if (!crazy_library_use_shared_relro(
    546            library.Get(), context, relro_start, relro_size, relro_fd)) {
    547     LOG_ERROR("%s: Could not use shared RELRO for %s: %s",
    548               __FUNCTION__,
    549               lib_name.c_str(),
    550               crazy_context_get_error(context));
    551     return false;
    552   }
    553 
    554   LOG_INFO("%s: Library %s using shared RELRO section!",
    555            __FUNCTION__,
    556            lib_name.c_str());
    557 
    558   return true;
    559 }
    560 
    561 jboolean CanUseSharedRelro(JNIEnv* env, jclass clazz) {
    562   return crazy_system_can_share_relro();
    563 }
    564 
    565 jlong GetRandomBaseLoadAddress(JNIEnv* env, jclass clazz, jlong bytes) {
    566   void* address =
    567       mmap(NULL, bytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    568   if (address == MAP_FAILED) {
    569     LOG_INFO("%s: Random base load address not determinable\n", __FUNCTION__);
    570     return 0;
    571   }
    572   munmap(address, bytes);
    573   LOG_INFO("%s: Random base load address is %p\n", __FUNCTION__, address);
    574   return static_cast<jlong>(reinterpret_cast<uintptr_t>(address));
    575 }
    576 
    577 const JNINativeMethod kNativeMethods[] = {
    578     {"nativeLoadLibrary",
    579      "("
    580      "Ljava/lang/String;"
    581      "J"
    582      "Lorg/chromium/base/library_loader/Linker$LibInfo;"
    583      ")"
    584      "Z",
    585      reinterpret_cast<void*>(&LoadLibrary)},
    586     {"nativeLoadLibraryInZipFile",
    587      "("
    588      "Ljava/lang/String;"
    589      "Ljava/lang/String;"
    590      "J"
    591      "Lorg/chromium/base/library_loader/Linker$LibInfo;"
    592      ")"
    593      "Z",
    594      reinterpret_cast<void*>(&LoadLibraryInZipFile)},
    595     {"nativeRunCallbackOnUiThread",
    596      "("
    597      "J"
    598      ")"
    599      "V",
    600      reinterpret_cast<void*>(&RunCallbackOnUiThread)},
    601     {"nativeCreateSharedRelro",
    602      "("
    603      "Ljava/lang/String;"
    604      "J"
    605      "Lorg/chromium/base/library_loader/Linker$LibInfo;"
    606      ")"
    607      "Z",
    608      reinterpret_cast<void*>(&CreateSharedRelro)},
    609     {"nativeUseSharedRelro",
    610      "("
    611      "Ljava/lang/String;"
    612      "Lorg/chromium/base/library_loader/Linker$LibInfo;"
    613      ")"
    614      "Z",
    615      reinterpret_cast<void*>(&UseSharedRelro)},
    616     {"nativeCanUseSharedRelro",
    617      "("
    618      ")"
    619      "Z",
    620      reinterpret_cast<void*>(&CanUseSharedRelro)},
    621     {"nativeGetRandomBaseLoadAddress",
    622      "("
    623      "J"
    624      ")"
    625      "J",
    626      reinterpret_cast<void*>(&GetRandomBaseLoadAddress)}, };
    627 
    628 }  // namespace
    629 
    630 // JNI_OnLoad() hook called when the linker library is loaded through
    631 // the regular System.LoadLibrary) API. This shall save the Java VM
    632 // handle and initialize LibInfo fields.
    633 jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    634   LOG_INFO("%s: Entering", __FUNCTION__);
    635   // Get new JNIEnv
    636   JNIEnv* env;
    637   if (JNI_OK != vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4)) {
    638     LOG_ERROR("Could not create JNIEnv");
    639     return -1;
    640   }
    641 
    642   // Register native methods.
    643   jclass linker_class;
    644   if (!InitClassReference(env,
    645                           "org/chromium/base/library_loader/Linker",
    646                           &linker_class))
    647     return -1;
    648 
    649   LOG_INFO("%s: Registering native methods", __FUNCTION__);
    650   env->RegisterNatives(linker_class,
    651                        kNativeMethods,
    652                        sizeof(kNativeMethods) / sizeof(kNativeMethods[0]));
    653 
    654   // Find LibInfo field ids.
    655   LOG_INFO("%s: Caching field IDs", __FUNCTION__);
    656   if (!s_lib_info_fields.Init(env)) {
    657     return -1;
    658   }
    659 
    660   // Resolve and save the Java side Linker callback class and method.
    661   LOG_INFO("%s: Resolving callback bindings", __FUNCTION__);
    662   if (!s_java_callback_bindings.Init(env, linker_class)) {
    663     return -1;
    664   }
    665 
    666   // Save JavaVM* handle into context.
    667   crazy_context_t* context = GetCrazyContext();
    668   crazy_context_set_java_vm(context, vm, JNI_VERSION_1_4);
    669 
    670   // Register the function that the crazy linker can call to post code
    671   // for later execution.
    672   crazy_context_set_callback_poster(context, &PostForLaterExecution, NULL);
    673 
    674   LOG_INFO("%s: Done", __FUNCTION__);
    675   return JNI_VERSION_1_4;
    676 }
    677