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.
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.
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.
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.
15 #include "linker_jni.h"
22 #include "legacy_linker_jni.h"
23 #include "modern_linker_jni.h"
25 namespace chromium_android_linker
{
27 // Variable containing LibInfo for the loaded library.
28 LibInfo_class s_lib_info_fields
;
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));
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().
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.
44 // https://crbug.com/508876
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_
);
52 env
->ReleaseStringUTFChars(str
, bytes
);
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
);
61 LOG_ERROR("Could not find class for %s", class_name
);
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
,
73 const char* field_name
,
74 const char* field_sig
,
76 *field_id
= env
->GetFieldID(clazz
, field_name
, field_sig
);
78 LOG_ERROR("Could not find ID for field '%s'", field_name
);
81 LOG_INFO("Found ID %p for field '%s'", *field_id
, field_name
);
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
,
91 const char* method_name
,
92 const char* method_sig
,
93 jmethodID
* method_id
) {
94 *method_id
= env
->GetStaticMethodID(clazz
, method_name
, method_sig
);
96 LOG_ERROR("Could not find ID for static method '%s'", method_name
);
99 LOG_INFO("Found ID %p for static method '%s'", *method_id
, method_name
);
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
,
109 const char* field_name
,
110 const char* field_sig
,
111 jfieldID
* field_id
) {
112 *field_id
= env
->GetStaticFieldID(clazz
, field_name
, field_sig
);
114 LOG_ERROR("Could not find ID for static field '%s'", field_name
);
117 LOG_INFO("Found ID %p for static field '%s'", *field_id
, field_name
);
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
,
130 if (!InitClassReference(env
, class_name
, &clazz
))
134 if (!InitStaticFieldId(env
, clazz
, field_name
, "I", &field_id
))
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
);
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
;
154 #if RESERVE_BREAKPAD_GUARD_REGION
155 // Pad the requested address space size for a Breakpad guard region.
156 bytes
+= kBreakpadGuardRegionBytes
;
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");
165 munmap(address
, bytes
);
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
);
173 LOG_INFO("Random base load address is %p", address
);
174 return static_cast<jlong
>(reinterpret_cast<uintptr_t>(address
));
179 const JNINativeMethod kNativeMethods
[] = {
180 {"nativeGetRandomBaseLoadAddress",
184 reinterpret_cast<void*>(&GetRandomBaseLoadAddress
)},
187 // JNI_OnLoad() initialization hook.
188 bool LinkerJNIInit(JavaVM
* vm
, JNIEnv
* env
) {
189 LOG_INFO("Entering");
191 // Register native methods.
193 if (!InitClassReference(env
,
194 "org/chromium/base/library_loader/Linker",
198 LOG_INFO("Registering native methods");
199 env
->RegisterNatives(linker_class
,
201 sizeof(kNativeMethods
) / sizeof(kNativeMethods
[0]));
203 // Find LibInfo field ids.
204 LOG_INFO("Caching field IDs");
205 if (!s_lib_info_fields
.Init(env
)) {
212 // JNI_OnLoad() hook called when the linker library is loaded through
213 // the regular System.LoadLibrary) API. This shall save the Java VM
214 // handle and initialize LibInfo fields.
215 jint
JNI_OnLoad(JavaVM
* vm
, void* reserved
) {
216 LOG_INFO("Entering");
219 if (JNI_OK
!= vm
->GetEnv(reinterpret_cast<void**>(&env
), JNI_VERSION_1_4
)) {
220 LOG_ERROR("Could not create JNIEnv");
224 // Initialize linker base and implementations.
225 if (!LinkerJNIInit(vm
, env
)
226 || !LegacyLinkerJNIInit(vm
, env
) || !ModernLinkerJNIInit(vm
, env
)) {
231 return JNI_VERSION_1_4
;
235 } // namespace chromium_android_linker
237 jint
JNI_OnLoad(JavaVM
* vm
, void* reserved
) {
238 return chromium_android_linker::JNI_OnLoad(vm
, reserved
);