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