• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
String(JNIEnv * env,jstring str)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|.
InitClassReference(JNIEnv * env,const char * class_name,jclass * 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|.
InitFieldId(JNIEnv * env,jclass clazz,const char * field_name,const char * field_sig,jfieldID * 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|.
InitStaticMethodId(JNIEnv * env,jclass clazz,const char * method_name,const char * method_sig,jmethodID * 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|.
InitStaticFieldId(JNIEnv * env,jclass clazz,const char * field_name,const char * field_sig,jfieldID * 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|.
InitStaticInt(JNIEnv * env,const char * class_name,const char * field_name,jint * 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.
GetRandomBaseLoadAddress(JNIEnv * env,jclass clazz)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.
LinkerJNIInit(JavaVM * vm,JNIEnv * env)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.
JNI_OnLoad(JavaVM * vm,void * reserved)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 
JNI_OnLoad(JavaVM * vm,void * reserved)239 jint JNI_OnLoad(JavaVM* vm, void* reserved) {
240   return chromium_android_linker::JNI_OnLoad(vm, reserved);
241 }
242