xref: /aosp_15_r20/external/libchrome/base/android/linker/linker_jni.cc (revision 635a864187cb8b6c713ff48b7e790a6b21769273)
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