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 <android/log.h>
16 #include <jni.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <sys/mman.h>
20
21 #include "build/build_config.h"
22
23 #include <crazy_linker.h>
24
25 // Set this to 1 to enable debug traces to the Android log.
26 // Note that LOG() from "base/logging.h" cannot be used, since it is
27 // in base/ which hasn't been loaded yet.
28 #define DEBUG 0
29
30 #define TAG "cr_ChromiumAndroidLinker"
31
32 #if DEBUG
33 #define LOG_INFO(FORMAT, ...) \
34 __android_log_print(ANDROID_LOG_INFO, TAG, "%s: " FORMAT, __FUNCTION__, \
35 ##__VA_ARGS__)
36 #else
37 #define LOG_INFO(FORMAT, ...) ((void)0)
38 #endif
39 #define LOG_ERROR(FORMAT, ...) \
40 __android_log_print(ANDROID_LOG_ERROR, TAG, "%s: " FORMAT, __FUNCTION__, \
41 ##__VA_ARGS__)
42
43 #define UNUSED __attribute__((unused))
44
45 // See commentary in crazy_linker_elf_loader.cpp for the effect of setting
46 // this. If changing there, change here also.
47 //
48 // For more, see:
49 // https://crbug.com/504410
50 #define RESERVE_BREAKPAD_GUARD_REGION 1
51
52 #if defined(ARCH_CPU_X86)
53 // Dalvik JIT generated code doesn't guarantee 16-byte stack alignment on
54 // x86 - use force_align_arg_pointer to realign the stack at the JNI
55 // boundary. https://crbug.com/655248
56 #define JNI_GENERATOR_EXPORT \
57 extern "C" __attribute__((visibility("default"), force_align_arg_pointer))
58 #else
59 #define JNI_GENERATOR_EXPORT extern "C" __attribute__((visibility("default")))
60 #endif
61
62 namespace chromium_android_linker {
63
64 namespace {
65
66 // Larger than the largest library we might attempt to load.
67 constexpr size_t kAddressSpaceReservationSize = 192 * 1024 * 1024;
68
69 // Size of any Breakpad guard region. 16MB is comfortably larger than the
70 // ~6MB relocation packing of the current 64-bit libchrome.so, the largest we
71 // expect to encounter.
72 #if RESERVE_BREAKPAD_GUARD_REGION
73 constexpr size_t kBreakpadGuardRegionBytes = 16 * 1024 * 1024;
74 #endif
75
76 // A simple scoped UTF String class that can be initialized from
77 // a Java jstring handle. Modeled like std::string, which cannot
78 // be used here.
79 class String {
80 public:
81 String(JNIEnv* env, jstring str);
82
~String()83 inline ~String() { ::free(ptr_); }
84
c_str() const85 inline const char* c_str() const { return ptr_ ? ptr_ : ""; }
size() const86 inline size_t size() const { return size_; }
87
88 private:
89 char* ptr_;
90 size_t size_;
91 };
92
93 // Simple scoped UTF String class constructor.
String(JNIEnv * env,jstring str)94 String::String(JNIEnv* env, jstring str) {
95 size_ = env->GetStringUTFLength(str);
96 ptr_ = static_cast<char*>(::malloc(size_ + 1));
97
98 // Note: This runs before browser native code is loaded, and so cannot
99 // rely on anything from base/. This means that we must use
100 // GetStringUTFChars() and not base::android::ConvertJavaStringToUTF8().
101 //
102 // GetStringUTFChars() suffices because the only strings used here are
103 // paths to APK files or names of shared libraries, all of which are
104 // plain ASCII, defined and hard-coded by the Chromium Android build.
105 //
106 // For more: see
107 // https://crbug.com/508876
108 //
109 // Note: GetStringUTFChars() returns Java UTF-8 bytes. This is good
110 // enough for the linker though.
111 const char* bytes = env->GetStringUTFChars(str, nullptr);
112 ::memcpy(ptr_, bytes, size_);
113 ptr_[size_] = '\0';
114
115 env->ReleaseStringUTFChars(str, bytes);
116 }
117
118 // Find the jclass JNI reference corresponding to a given |class_name|.
119 // |env| is the current JNI environment handle.
120 // On success, return true and set |*clazz|.
InitClassReference(JNIEnv * env,const char * class_name,jclass * clazz)121 bool InitClassReference(JNIEnv* env, const char* class_name, jclass* clazz) {
122 *clazz = env->FindClass(class_name);
123 if (!*clazz) {
124 LOG_ERROR("Could not find class for %s", class_name);
125 return false;
126 }
127 return true;
128 }
129
130 // Initialize a jfieldID corresponding to the field of a given |clazz|,
131 // with name |field_name| and signature |field_sig|.
132 // |env| is the current JNI environment handle.
133 // On success, return true and set |*field_id|.
InitFieldId(JNIEnv * env,jclass clazz,const char * field_name,const char * field_sig,jfieldID * field_id)134 bool InitFieldId(JNIEnv* env,
135 jclass clazz,
136 const char* field_name,
137 const char* field_sig,
138 jfieldID* field_id) {
139 *field_id = env->GetFieldID(clazz, field_name, field_sig);
140 if (!*field_id) {
141 LOG_ERROR("Could not find ID for field '%s'", field_name);
142 return false;
143 }
144 LOG_INFO("Found ID %p for field '%s'", *field_id, field_name);
145 return true;
146 }
147
148 // Initialize a jmethodID corresponding to the static method of a given
149 // |clazz|, with name |method_name| and signature |method_sig|.
150 // |env| is the current JNI environment handle.
151 // On success, return true and set |*method_id|.
InitStaticMethodId(JNIEnv * env,jclass clazz,const char * method_name,const char * method_sig,jmethodID * method_id)152 bool InitStaticMethodId(JNIEnv* env,
153 jclass clazz,
154 const char* method_name,
155 const char* method_sig,
156 jmethodID* method_id) {
157 *method_id = env->GetStaticMethodID(clazz, method_name, method_sig);
158 if (!*method_id) {
159 LOG_ERROR("Could not find ID for static method '%s'", method_name);
160 return false;
161 }
162 LOG_INFO("Found ID %p for static method '%s'", *method_id, method_name);
163 return true;
164 }
165
166 // Initialize a jfieldID corresponding to the static field of a given |clazz|,
167 // with name |field_name| and signature |field_sig|.
168 // |env| is the current JNI environment handle.
169 // On success, return true and set |*field_id|.
InitStaticFieldId(JNIEnv * env,jclass clazz,const char * field_name,const char * field_sig,jfieldID * field_id)170 bool InitStaticFieldId(JNIEnv* env,
171 jclass clazz,
172 const char* field_name,
173 const char* field_sig,
174 jfieldID* field_id) {
175 *field_id = env->GetStaticFieldID(clazz, field_name, field_sig);
176 if (!*field_id) {
177 LOG_ERROR("Could not find ID for static field '%s'", field_name);
178 return false;
179 }
180 LOG_INFO("Found ID %p for static field '%s'", *field_id, field_name);
181 return true;
182 }
183
184 // Initialize a jint corresponding to the static integer field of a class
185 // with class name |class_name| and field name |field_name|.
186 // |env| is the current JNI environment handle.
187 // On success, return true and set |*value|.
InitStaticInt(JNIEnv * env,const char * class_name,const char * field_name,jint * value)188 bool InitStaticInt(JNIEnv* env,
189 const char* class_name,
190 const char* field_name,
191 jint* value) {
192 jclass clazz;
193 if (!InitClassReference(env, class_name, &clazz))
194 return false;
195
196 jfieldID field_id;
197 if (!InitStaticFieldId(env, clazz, field_name, "I", &field_id))
198 return false;
199
200 *value = env->GetStaticIntField(clazz, field_id);
201 LOG_INFO("Found value %d for class '%s', static field '%s'",
202 *value, class_name, field_name);
203
204 return true;
205 }
206
207 // A class used to model the field IDs of the org.chromium.base.Linker
208 // LibInfo inner class, used to communicate data with the Java side
209 // of the linker.
210 struct LibInfo_class {
211 jfieldID load_address_id;
212 jfieldID load_size_id;
213 jfieldID relro_start_id;
214 jfieldID relro_size_id;
215 jfieldID relro_fd_id;
216
217 // Initialize an instance.
Initchromium_android_linker::__anonf6ad9cf20111::LibInfo_class218 bool Init(JNIEnv* env) {
219 jclass clazz;
220 if (!InitClassReference(
221 env, "org/chromium/base/library_loader/Linker$LibInfo", &clazz)) {
222 return false;
223 }
224
225 return InitFieldId(env, clazz, "mLoadAddress", "J", &load_address_id) &&
226 InitFieldId(env, clazz, "mLoadSize", "J", &load_size_id) &&
227 InitFieldId(env, clazz, "mRelroStart", "J", &relro_start_id) &&
228 InitFieldId(env, clazz, "mRelroSize", "J", &relro_size_id) &&
229 InitFieldId(env, clazz, "mRelroFd", "I", &relro_fd_id);
230 }
231
SetLoadInfochromium_android_linker::__anonf6ad9cf20111::LibInfo_class232 void SetLoadInfo(JNIEnv* env,
233 jobject library_info_obj,
234 size_t load_address,
235 size_t load_size) {
236 env->SetLongField(library_info_obj, load_address_id, load_address);
237 env->SetLongField(library_info_obj, load_size_id, load_size);
238 }
239
SetRelroInfochromium_android_linker::__anonf6ad9cf20111::LibInfo_class240 void SetRelroInfo(JNIEnv* env,
241 jobject library_info_obj,
242 size_t relro_start,
243 size_t relro_size,
244 int relro_fd) {
245 env->SetLongField(library_info_obj, relro_start_id, relro_start);
246 env->SetLongField(library_info_obj, relro_size_id, relro_size);
247 env->SetIntField(library_info_obj, relro_fd_id, relro_fd);
248 }
249
250 // Use this instance to convert a RelroInfo reference into
251 // a crazy_library_info_t.
GetRelroInfochromium_android_linker::__anonf6ad9cf20111::LibInfo_class252 void GetRelroInfo(JNIEnv* env,
253 jobject library_info_obj,
254 size_t* relro_start,
255 size_t* relro_size,
256 int* relro_fd) {
257 if (relro_start) {
258 *relro_start = static_cast<size_t>(
259 env->GetLongField(library_info_obj, relro_start_id));
260 }
261
262 if (relro_size) {
263 *relro_size = static_cast<size_t>(
264 env->GetLongField(library_info_obj, relro_size_id));
265 }
266
267 if (relro_fd) {
268 *relro_fd = env->GetIntField(library_info_obj, relro_fd_id);
269 }
270 }
271 };
272
273 // Variable containing LibInfo for the loaded library.
274 LibInfo_class s_lib_info_fields;
275
276 // Return true iff |address| is a valid address for the target CPU.
IsValidAddress(jlong address)277 inline bool IsValidAddress(jlong address) {
278 return static_cast<jlong>(static_cast<size_t>(address)) == address;
279 }
280
281 // The linker uses a single crazy_context_t object created on demand.
282 // There is no need to protect this against concurrent access, locking
283 // is already handled on the Java side.
GetCrazyContext()284 crazy_context_t* GetCrazyContext() {
285 static crazy_context_t* s_crazy_context = nullptr;
286
287 if (!s_crazy_context) {
288 // Create new context.
289 s_crazy_context = crazy_context_create();
290
291 // Ensure libraries located in the same directory as the linker
292 // can be loaded before system ones.
293 crazy_context_add_search_path_for_address(
294 s_crazy_context, reinterpret_cast<void*>(&GetCrazyContext));
295 }
296
297 return s_crazy_context;
298 }
299
300 // A scoped crazy_library_t that automatically closes the handle
301 // on scope exit, unless Release() has been called.
302 class ScopedLibrary {
303 public:
ScopedLibrary()304 ScopedLibrary() : lib_(nullptr) {}
305
~ScopedLibrary()306 ~ScopedLibrary() {
307 if (lib_)
308 crazy_library_close_with_context(lib_, GetCrazyContext());
309 }
310
Get()311 crazy_library_t* Get() { return lib_; }
312
GetPtr()313 crazy_library_t** GetPtr() { return &lib_; }
314
Release()315 crazy_library_t* Release() {
316 crazy_library_t* ret = lib_;
317 lib_ = nullptr;
318 return ret;
319 }
320
321 private:
322 crazy_library_t* lib_;
323 };
324
325 // Retrieve the SDK build version and pass it into the crazy linker. This
326 // needs to be done early in initialization, before any other crazy linker
327 // code is run.
328 // |env| is the current JNI environment handle.
329 // On success, return true.
InitSDKVersionInfo(JNIEnv * env)330 bool InitSDKVersionInfo(JNIEnv* env) {
331 jint value = 0;
332 if (!InitStaticInt(env, "android/os/Build$VERSION", "SDK_INT", &value))
333 return false;
334
335 crazy_set_sdk_build_version(static_cast<int>(value));
336 LOG_INFO("Set SDK build version to %d", static_cast<int>(value));
337
338 return true;
339 }
340
341 } // namespace
342
343 // Use Android ASLR to create a random address into which we expect to be
344 // able to load libraries. Note that this is probabilistic; we unmap the
345 // address we get from mmap and assume we can re-map into it later. This
346 // works the majority of the time. If it doesn't, client code backs out and
347 // then loads the library normally at any available address.
348 // |env| is the current JNI environment handle, and |clazz| a class.
349 // Returns the address selected by ASLR, or 0 on error.
350 JNI_GENERATOR_EXPORT jlong
Java_org_chromium_base_library_1loader_Linker_nativeGetRandomBaseLoadAddress(JNIEnv * env,jclass clazz)351 Java_org_chromium_base_library_1loader_Linker_nativeGetRandomBaseLoadAddress(
352 JNIEnv* env,
353 jclass clazz) {
354 size_t bytes = kAddressSpaceReservationSize;
355
356 #if RESERVE_BREAKPAD_GUARD_REGION
357 // Pad the requested address space size for a Breakpad guard region.
358 bytes += kBreakpadGuardRegionBytes;
359 #endif
360
361 void* address =
362 mmap(nullptr, bytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
363 if (address == MAP_FAILED) {
364 LOG_INFO("Random base load address not determinable");
365 return 0;
366 }
367 munmap(address, bytes);
368
369 #if RESERVE_BREAKPAD_GUARD_REGION
370 // Allow for a Breakpad guard region ahead of the returned address.
371 address = reinterpret_cast<void*>(
372 reinterpret_cast<uintptr_t>(address) + kBreakpadGuardRegionBytes);
373 #endif
374
375 LOG_INFO("Random base load address is %p", address);
376 return static_cast<jlong>(reinterpret_cast<uintptr_t>(address));
377 }
378
379 // We identify the abi tag for which the linker is running. This allows
380 // us to select the library which matches the abi of the linker.
381
382 #if defined(__arm__) && defined(__ARM_ARCH_7A__)
383 #define CURRENT_ABI "armeabi-v7a"
384 #elif defined(__arm__)
385 #define CURRENT_ABI "armeabi"
386 #elif defined(__i386__)
387 #define CURRENT_ABI "x86"
388 #elif defined(__mips__)
389 #define CURRENT_ABI "mips"
390 #elif defined(__x86_64__)
391 #define CURRENT_ABI "x86_64"
392 #elif defined(__aarch64__)
393 #define CURRENT_ABI "arm64-v8a"
394 #else
395 #error "Unsupported target abi"
396 #endif
397
398 // Add a zip archive file path to the context's current search path
399 // list. Making it possible to load libraries directly from it.
400 JNI_GENERATOR_EXPORT bool
Java_org_chromium_base_library_1loader_Linker_nativeAddZipArchivePath(JNIEnv * env,jclass clazz,jstring apk_path_obj)401 Java_org_chromium_base_library_1loader_Linker_nativeAddZipArchivePath(
402 JNIEnv* env,
403 jclass clazz,
404 jstring apk_path_obj) {
405 String apk_path(env, apk_path_obj);
406
407 char search_path[512];
408 snprintf(search_path, sizeof(search_path), "%s!lib/" CURRENT_ABI "/",
409 apk_path.c_str());
410
411 crazy_context_t* context = GetCrazyContext();
412 crazy_context_add_search_path(context, search_path);
413 return true;
414 }
415
416 // Load a library with the chromium linker. This will also call its
417 // JNI_OnLoad() method, which shall register its methods. Note that
418 // lazy native method resolution will _not_ work after this, because
419 // Dalvik uses the system's dlsym() which won't see the new library,
420 // so explicit registration is mandatory.
421 //
422 // |env| is the current JNI environment handle.
423 // |clazz| is the static class handle for org.chromium.base.Linker,
424 // and is ignored here.
425 // |library_name| is the library name (e.g. libfoo.so).
426 // |load_address| is an explicit load address.
427 // |lib_info_obj| is a LibInfo handle used to communicate information
428 // with the Java side.
429 // Return true on success.
430 JNI_GENERATOR_EXPORT bool
Java_org_chromium_base_library_1loader_Linker_nativeLoadLibrary(JNIEnv * env,jclass clazz,jstring lib_name_obj,jlong load_address,jobject lib_info_obj)431 Java_org_chromium_base_library_1loader_Linker_nativeLoadLibrary(
432 JNIEnv* env,
433 jclass clazz,
434 jstring lib_name_obj,
435 jlong load_address,
436 jobject lib_info_obj) {
437 String library_name(env, lib_name_obj);
438 LOG_INFO("Called for %s, at address 0x%llx", library_name, load_address);
439 crazy_context_t* context = GetCrazyContext();
440
441 if (!IsValidAddress(load_address)) {
442 LOG_ERROR("Invalid address 0x%llx",
443 static_cast<unsigned long long>(load_address));
444 return false;
445 }
446
447 // Set the desired load address (0 means randomize it).
448 crazy_context_set_load_address(context, static_cast<size_t>(load_address));
449
450 ScopedLibrary library;
451 if (!crazy_library_open(library.GetPtr(), library_name.c_str(), context)) {
452 return false;
453 }
454
455 crazy_library_info_t info;
456 if (!crazy_library_get_info(library.Get(), context, &info)) {
457 LOG_ERROR("Could not get library information for %s: %s",
458 library_name.c_str(), crazy_context_get_error(context));
459 return false;
460 }
461
462 // Release library object to keep it alive after the function returns.
463 library.Release();
464
465 s_lib_info_fields.SetLoadInfo(env, lib_info_obj, info.load_address,
466 info.load_size);
467 LOG_INFO("Success loading library %s", library_name.c_str());
468 return true;
469 }
470
471 // Class holding the Java class and method ID for the Java side Linker
472 // postCallbackOnMainThread method.
473 struct JavaCallbackBindings_class {
474 jclass clazz;
475 jmethodID method_id;
476
477 // Initialize an instance.
Initchromium_android_linker::JavaCallbackBindings_class478 bool Init(JNIEnv* env, jclass linker_class) {
479 clazz = reinterpret_cast<jclass>(env->NewGlobalRef(linker_class));
480 return InitStaticMethodId(env, linker_class, "postCallbackOnMainThread",
481 "(J)V", &method_id);
482 }
483 };
484
485 static JavaCallbackBindings_class s_java_callback_bindings;
486
487 // Designated receiver function for callbacks from Java. Its name is known
488 // to the Java side.
489 // |env| is the current JNI environment handle and is ignored here.
490 // |clazz| is the static class handle for org.chromium.base.Linker,
491 // and is ignored here.
492 // |arg| is a pointer to an allocated crazy_callback_t, deleted after use.
493 JNI_GENERATOR_EXPORT void
Java_org_chromium_base_library_1loader_Linker_nativeRunCallbackOnUiThread(JNIEnv * env,jclass clazz,jlong arg)494 Java_org_chromium_base_library_1loader_Linker_nativeRunCallbackOnUiThread(
495 JNIEnv* env,
496 jclass clazz,
497 jlong arg) {
498 crazy_callback_t* callback = reinterpret_cast<crazy_callback_t*>(arg);
499
500 LOG_INFO("Called back from java with handler %p, opaque %p",
501 callback->handler, callback->opaque);
502
503 crazy_callback_run(callback);
504 delete callback;
505 }
506
507 // Request a callback from Java. The supplied crazy_callback_t is valid only
508 // for the duration of this call, so we copy it to a newly allocated
509 // crazy_callback_t and then call the Java side's postCallbackOnMainThread.
510 // This will call back to to our RunCallbackOnUiThread some time
511 // later on the UI thread.
512 // |callback_request| is a crazy_callback_t.
513 // |poster_opaque| is unused.
514 // Returns true if the callback request succeeds.
PostForLaterExecution(crazy_callback_t * callback_request,void * poster_opaque UNUSED)515 static bool PostForLaterExecution(crazy_callback_t* callback_request,
516 void* poster_opaque UNUSED) {
517 crazy_context_t* context = GetCrazyContext();
518
519 JavaVM* vm;
520 int minimum_jni_version;
521 crazy_context_get_java_vm(context, reinterpret_cast<void**>(&vm),
522 &minimum_jni_version);
523
524 // Do not reuse JNIEnv from JNI_OnLoad, but retrieve our own.
525 JNIEnv* env;
526 if (JNI_OK !=
527 vm->GetEnv(reinterpret_cast<void**>(&env), minimum_jni_version)) {
528 LOG_ERROR("Could not create JNIEnv");
529 return false;
530 }
531
532 // Copy the callback; the one passed as an argument may be temporary.
533 crazy_callback_t* callback = new crazy_callback_t();
534 *callback = *callback_request;
535
536 LOG_INFO("Calling back to java with handler %p, opaque %p", callback->handler,
537 callback->opaque);
538
539 jlong arg = static_cast<jlong>(reinterpret_cast<uintptr_t>(callback));
540
541 env->CallStaticVoidMethod(s_java_callback_bindings.clazz,
542 s_java_callback_bindings.method_id, arg);
543
544 // Back out and return false if we encounter a JNI exception.
545 if (env->ExceptionCheck() == JNI_TRUE) {
546 env->ExceptionDescribe();
547 env->ExceptionClear();
548 delete callback;
549 return false;
550 }
551
552 return true;
553 }
554
555 JNI_GENERATOR_EXPORT jboolean
Java_org_chromium_base_library_1loader_Linker_nativeCreateSharedRelro(JNIEnv * env,jclass clazz,jstring library_name,jlong load_address,jobject lib_info_obj)556 Java_org_chromium_base_library_1loader_Linker_nativeCreateSharedRelro(
557 JNIEnv* env,
558 jclass clazz,
559 jstring library_name,
560 jlong load_address,
561 jobject lib_info_obj) {
562 String lib_name(env, library_name);
563
564 LOG_INFO("Called for %s", lib_name.c_str());
565
566 if (!IsValidAddress(load_address)) {
567 LOG_ERROR("Invalid address 0x%llx",
568 static_cast<unsigned long long>(load_address));
569 return false;
570 }
571
572 ScopedLibrary library;
573 if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
574 LOG_ERROR("Could not find %s", lib_name.c_str());
575 return false;
576 }
577
578 crazy_context_t* context = GetCrazyContext();
579 size_t relro_start = 0;
580 size_t relro_size = 0;
581 int relro_fd = -1;
582
583 if (!crazy_library_create_shared_relro(
584 library.Get(), context, static_cast<size_t>(load_address),
585 &relro_start, &relro_size, &relro_fd)) {
586 LOG_ERROR("Could not create shared RELRO sharing for %s: %s\n",
587 lib_name.c_str(), crazy_context_get_error(context));
588 return false;
589 }
590
591 s_lib_info_fields.SetRelroInfo(env, lib_info_obj, relro_start, relro_size,
592 relro_fd);
593 return true;
594 }
595
596 JNI_GENERATOR_EXPORT jboolean
Java_org_chromium_base_library_1loader_Linker_nativeUseSharedRelro(JNIEnv * env,jclass clazz,jstring library_name,jobject lib_info_obj)597 Java_org_chromium_base_library_1loader_Linker_nativeUseSharedRelro(
598 JNIEnv* env,
599 jclass clazz,
600 jstring library_name,
601 jobject lib_info_obj) {
602 String lib_name(env, library_name);
603
604 LOG_INFO("Called for %s, lib_info_ref=%p", lib_name.c_str(), lib_info_obj);
605
606 ScopedLibrary library;
607 if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
608 LOG_ERROR("Could not find %s", lib_name.c_str());
609 return false;
610 }
611
612 crazy_context_t* context = GetCrazyContext();
613 size_t relro_start = 0;
614 size_t relro_size = 0;
615 int relro_fd = -1;
616 s_lib_info_fields.GetRelroInfo(env, lib_info_obj, &relro_start, &relro_size,
617 &relro_fd);
618
619 LOG_INFO("library=%s relro start=%p size=%p fd=%d", lib_name.c_str(),
620 (void*)relro_start, (void*)relro_size, relro_fd);
621
622 if (!crazy_library_use_shared_relro(library.Get(), context, relro_start,
623 relro_size, relro_fd)) {
624 LOG_ERROR("Could not use shared RELRO for %s: %s", lib_name.c_str(),
625 crazy_context_get_error(context));
626 return false;
627 }
628
629 LOG_INFO("Library %s using shared RELRO section!", lib_name.c_str());
630
631 return true;
632 }
633
LinkerJNIInit(JavaVM * vm,JNIEnv * env)634 static bool LinkerJNIInit(JavaVM* vm, JNIEnv* env) {
635 LOG_INFO("Entering");
636
637 // Initialize SDK version info.
638 LOG_INFO("Retrieving SDK version info");
639 if (!InitSDKVersionInfo(env))
640 return false;
641
642 // Find LibInfo field ids.
643 LOG_INFO("Caching field IDs");
644 if (!s_lib_info_fields.Init(env)) {
645 return false;
646 }
647
648 // Register native methods.
649 jclass linker_class;
650 if (!InitClassReference(env, "org/chromium/base/library_loader/Linker",
651 &linker_class))
652 return false;
653
654 // Resolve and save the Java side Linker callback class and method.
655 LOG_INFO("Resolving callback bindings");
656 if (!s_java_callback_bindings.Init(env, linker_class)) {
657 return false;
658 }
659
660 // Save JavaVM* handle into context.
661 crazy_context_t* context = GetCrazyContext();
662 crazy_context_set_java_vm(context, vm, JNI_VERSION_1_4);
663
664 // Register the function that the crazy linker can call to post code
665 // for later execution.
666 crazy_context_set_callback_poster(context, &PostForLaterExecution, nullptr);
667
668 return true;
669 }
670
671 // JNI_OnLoad() hook called when the linker library is loaded through
672 // the regular System.LoadLibrary) API. This shall save the Java VM
673 // handle and initialize LibInfo fields.
JNI_OnLoad(JavaVM * vm,void * reserved)674 jint JNI_OnLoad(JavaVM* vm, void* reserved) {
675 LOG_INFO("Entering");
676 // Get new JNIEnv
677 JNIEnv* env;
678 if (JNI_OK != vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4)) {
679 LOG_ERROR("Could not create JNIEnv");
680 return -1;
681 }
682
683 // Initialize linker base and implementations.
684 if (!LinkerJNIInit(vm, env)) {
685 return -1;
686 }
687
688 LOG_INFO("Done");
689 return JNI_VERSION_1_4;
690 }
691
692 } // namespace chromium_android_linker
693
JNI_OnLoad(JavaVM * vm,void * reserved)694 jint JNI_OnLoad(JavaVM* vm, void* reserved) {
695 return chromium_android_linker::JNI_OnLoad(vm, reserved);
696 }
697