Home | History | Annotate | Download | only in linker
      1 // Copyright 2015 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.
      8 
      9 // The main point of this linker is to be able to share the RELRO
     10 // section of libchrome.so (or equivalent) between renderer processes.
     11 
     12 // This source code *cannot* depend on anything from base/ or the C++
     13 // STL, to keep the final library small, and avoid ugly dependency issues.
     14 
     15 #include "linker_jni.h"
     16 
     17 #include <sys/mman.h>
     18 #include <jni.h>
     19 #include <stdlib.h>
     20 #include <string.h>
     21 
     22 #include "legacy_linker_jni.h"
     23 #include "modern_linker_jni.h"
     24 
     25 namespace chromium_android_linker {
     26 
     27 // Variable containing LibInfo for the loaded library.
     28 LibInfo_class s_lib_info_fields;
     29 
     30 // Simple scoped UTF String class constructor.
     31 String::String(JNIEnv* env, jstring str) {
     32   size_ = env->GetStringUTFLength(str);
     33   ptr_ = static_cast<char*>(::malloc(size_ + 1));
     34 
     35   // Note: This runs before browser native code is loaded, and so cannot
     36   // rely on anything from base/. This means that we must use
     37   // GetStringUTFChars() and not base::android::ConvertJavaStringToUTF8().
     38   //
     39   // GetStringUTFChars() suffices because the only strings used here are
     40   // paths to APK files or names of shared libraries, all of which are
     41   // plain ASCII, defined and hard-coded by the Chromium Android build.
     42   //
     43   // For more: see
     44   //   https://crbug.com/508876
     45   //
     46   // Note: GetStringUTFChars() returns Java UTF-8 bytes. This is good
     47   // enough for the linker though.
     48   const char* bytes = env->GetStringUTFChars(str, nullptr);
     49   ::memcpy(ptr_, bytes, size_);
     50   ptr_[size_] = '\0';
     51 
     52   env->ReleaseStringUTFChars(str, bytes);
     53 }
     54 
     55 // Find the jclass JNI reference corresponding to a given |class_name|.
     56 // |env| is the current JNI environment handle.
     57 // On success, return true and set |*clazz|.
     58 bool InitClassReference(JNIEnv* env, const char* class_name, jclass* clazz) {
     59   *clazz = env->FindClass(class_name);
     60   if (!*clazz) {
     61     LOG_ERROR("Could not find class for %s", class_name);
     62     return false;
     63   }
     64   return true;
     65 }
     66 
     67 // Initialize a jfieldID corresponding to the field of a given |clazz|,
     68 // with name |field_name| and signature |field_sig|.
     69 // |env| is the current JNI environment handle.
     70 // On success, return true and set |*field_id|.
     71 bool InitFieldId(JNIEnv* env,
     72                  jclass clazz,
     73                  const char* field_name,
     74                  const char* field_sig,
     75                  jfieldID* field_id) {
     76   *field_id = env->GetFieldID(clazz, field_name, field_sig);
     77   if (!*field_id) {
     78     LOG_ERROR("Could not find ID for field '%s'", field_name);
     79     return false;
     80   }
     81   LOG_INFO("Found ID %p for field '%s'", *field_id, field_name);
     82   return true;
     83 }
     84 
     85 // Initialize a jmethodID corresponding to the static method of a given
     86 // |clazz|, with name |method_name| and signature |method_sig|.
     87 // |env| is the current JNI environment handle.
     88 // On success, return true and set |*method_id|.
     89 bool InitStaticMethodId(JNIEnv* env,
     90                         jclass clazz,
     91                         const char* method_name,
     92                         const char* method_sig,
     93                         jmethodID* method_id) {
     94   *method_id = env->GetStaticMethodID(clazz, method_name, method_sig);
     95   if (!*method_id) {
     96     LOG_ERROR("Could not find ID for static method '%s'", method_name);
     97     return false;
     98   }
     99   LOG_INFO("Found ID %p for static method '%s'", *method_id, method_name);
    100   return true;
    101 }
    102 
    103 // Initialize a jfieldID corresponding to the static field of a given |clazz|,
    104 // with name |field_name| and signature |field_sig|.
    105 // |env| is the current JNI environment handle.
    106 // On success, return true and set |*field_id|.
    107 bool InitStaticFieldId(JNIEnv* env,
    108                        jclass clazz,
    109                        const char* field_name,
    110                        const char* field_sig,
    111                        jfieldID* field_id) {
    112   *field_id = env->GetStaticFieldID(clazz, field_name, field_sig);
    113   if (!*field_id) {
    114     LOG_ERROR("Could not find ID for static field '%s'", field_name);
    115     return false;
    116   }
    117   LOG_INFO("Found ID %p for static field '%s'", *field_id, field_name);
    118   return true;
    119 }
    120 
    121 // Initialize a jint corresponding to the static integer field of a class
    122 // with class name |class_name| and field name |field_name|.
    123 // |env| is the current JNI environment handle.
    124 // On success, return true and set |*value|.
    125 bool InitStaticInt(JNIEnv* env,
    126                    const char* class_name,
    127                    const char* field_name,
    128                    jint* value) {
    129   jclass clazz;
    130   if (!InitClassReference(env, class_name, &clazz))
    131     return false;
    132 
    133   jfieldID field_id;
    134   if (!InitStaticFieldId(env, clazz, field_name, "I", &field_id))
    135     return false;
    136 
    137   *value = env->GetStaticIntField(clazz, field_id);
    138   LOG_INFO("Found value %d for class '%s', static field '%s'",
    139            *value, class_name, field_name);
    140 
    141   return true;
    142 }
    143 
    144 // Use Android ASLR to create a random address into which we expect to be
    145 // able to load libraries. Note that this is probabilistic; we unmap the
    146 // address we get from mmap and assume we can re-map into it later. This
    147 // works the majority of the time. If it doesn't, client code backs out and
    148 // then loads the library normally at any available address.
    149 // |env| is the current JNI environment handle, and |clazz| a class.
    150 // Returns the address selected by ASLR, or 0 on error.
    151 jlong GetRandomBaseLoadAddress(JNIEnv* env, jclass clazz) {
    152   size_t bytes = kAddressSpaceReservationSize;
    153 
    154 #if RESERVE_BREAKPAD_GUARD_REGION
    155   // Pad the requested address space size for a Breakpad guard region.
    156   bytes += kBreakpadGuardRegionBytes;
    157 #endif
    158 
    159   void* address =
    160       mmap(nullptr, bytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
    161   if (address == MAP_FAILED) {
    162     LOG_INFO("Random base load address not determinable");
    163     return 0;
    164   }
    165   munmap(address, bytes);
    166 
    167 #if RESERVE_BREAKPAD_GUARD_REGION
    168   // Allow for a Breakpad guard region ahead of the returned address.
    169   address = reinterpret_cast<void*>(
    170       reinterpret_cast<uintptr_t>(address) + kBreakpadGuardRegionBytes);
    171 #endif
    172 
    173   LOG_INFO("Random base load address is %p", address);
    174   return static_cast<jlong>(reinterpret_cast<uintptr_t>(address));
    175 }
    176 
    177 namespace {
    178 
    179 const JNINativeMethod kNativeMethods[] = {
    180     {"nativeGetRandomBaseLoadAddress",
    181      "("
    182      ")"
    183      "J",
    184      reinterpret_cast<void*>(&GetRandomBaseLoadAddress)},
    185 };
    186 
    187 const size_t kNumNativeMethods =
    188     sizeof(kNativeMethods) / sizeof(kNativeMethods[0]);
    189 
    190 // JNI_OnLoad() initialization hook.
    191 bool LinkerJNIInit(JavaVM* vm, JNIEnv* env) {
    192   LOG_INFO("Entering");
    193 
    194   // Register native methods.
    195   jclass linker_class;
    196   if (!InitClassReference(env,
    197                           "org/chromium/base/library_loader/Linker",
    198                           &linker_class))
    199     return false;
    200 
    201   LOG_INFO("Registering native methods");
    202   if (env->RegisterNatives(linker_class, kNativeMethods, kNumNativeMethods) < 0)
    203     return false;
    204 
    205   // Find LibInfo field ids.
    206   LOG_INFO("Caching field IDs");
    207   if (!s_lib_info_fields.Init(env)) {
    208     return false;
    209   }
    210 
    211   return true;
    212 }
    213 
    214 // JNI_OnLoad() hook called when the linker library is loaded through
    215 // the regular System.LoadLibrary) API. This shall save the Java VM
    216 // handle and initialize LibInfo fields.
    217 jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    218   LOG_INFO("Entering");
    219   // Get new JNIEnv
    220   JNIEnv* env;
    221   if (JNI_OK != vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4)) {
    222     LOG_ERROR("Could not create JNIEnv");
    223     return -1;
    224   }
    225 
    226   // Initialize linker base and implementations.
    227   if (!LinkerJNIInit(vm, env)
    228       || !LegacyLinkerJNIInit(vm, env) || !ModernLinkerJNIInit(vm, env)) {
    229     return -1;
    230   }
    231 
    232   LOG_INFO("Done");
    233   return JNI_VERSION_1_4;
    234 }
    235 
    236 }  // namespace
    237 }  // namespace chromium_android_linker
    238 
    239 jint JNI_OnLoad(JavaVM* vm, void* reserved) {
    240   return chromium_android_linker::JNI_OnLoad(vm, reserved);
    241 }
    242