1 // Copyright 2014 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 (e.g. libcontentshell.so).
9 // The main point of this linker is to be able to share the RELRO
10 // section of libcontentshell.so (or equivalent) between the browser and
13 // This source code *cannot* depend on anything from base/ or the C++
14 // STL, to keep the final library small, and avoid ugly dependency issues.
16 #include <android/log.h>
17 #include <crazy_linker.h>
25 // See commentary in crazy_linker_elf_loader.cpp for the effect of setting
26 // this. If changing there, change here also.
29 // https://crbug.com/504410
30 #define RESERVE_BREAKPAD_GUARD_REGION 1
32 // Set this to 1 to enable debug traces to the Android log.
33 // Note that LOG() from "base/logging.h" cannot be used, since it is
34 // in base/ which hasn't been loaded yet.
37 #define TAG "chromium_android_linker"
40 #define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
42 #define LOG_INFO(...) ((void)0)
44 #define LOG_ERROR(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
46 #define UNUSED __attribute__((unused))
50 // A simply scoped UTF String class that can be initialized from
51 // a Java jstring handle. Modeled like std::string, which cannot
55 String(JNIEnv
* env
, jstring str
);
62 const char* c_str() const { return ptr_
? ptr_
: ""; }
63 size_t size() const { return size_
; }
70 String::String(JNIEnv
* env
, jstring str
) {
71 size_
= env
->GetStringUTFLength(str
);
72 ptr_
= static_cast<char*>(::malloc(size_
+ 1));
74 // Note: This runs before browser native code is loaded, and so cannot
75 // rely on anything from base/. This means that we must use
76 // GetStringUTFChars() and not base::android::ConvertJavaStringToUTF8().
78 // GetStringUTFChars() suffices because the only strings used here are
79 // paths to APK files or names of shared libraries, all of which are
80 // plain ASCII, defined and hard-coded by the Chromium Android build.
83 // https://crbug.com/508876
85 // Note: GetStringUTFChars() returns Java UTF-8 bytes. This is good
86 // enough for the linker though.
87 const char* bytes
= env
->GetStringUTFChars(str
, NULL
);
88 ::memcpy(ptr_
, bytes
, size_
);
91 env
->ReleaseStringUTFChars(str
, bytes
);
94 // Return true iff |address| is a valid address for the target CPU.
95 bool IsValidAddress(jlong address
) {
96 return static_cast<jlong
>(static_cast<size_t>(address
)) == address
;
99 // Find the jclass JNI reference corresponding to a given |class_name|.
100 // |env| is the current JNI environment handle.
101 // On success, return true and set |*clazz|.
102 bool InitClassReference(JNIEnv
* env
, const char* class_name
, jclass
* clazz
) {
103 *clazz
= env
->FindClass(class_name
);
105 LOG_ERROR("Could not find class for %s", class_name
);
111 // Initialize a jfieldID corresponding to the field of a given |clazz|,
112 // with name |field_name| and signature |field_sig|.
113 // |env| is the current JNI environment handle.
114 // On success, return true and set |*field_id|.
115 bool InitFieldId(JNIEnv
* env
,
117 const char* field_name
,
118 const char* field_sig
,
119 jfieldID
* field_id
) {
120 *field_id
= env
->GetFieldID(clazz
, field_name
, field_sig
);
122 LOG_ERROR("Could not find ID for field '%s'", field_name
);
126 "%s: Found ID %p for field '%s'", __FUNCTION__
, *field_id
, field_name
);
130 // Initialize a jmethodID corresponding to the static method of a given
131 // |clazz|, with name |method_name| and signature |method_sig|.
132 // |env| is the current JNI environment handle.
133 // On success, return true and set |*method_id|.
134 bool InitStaticMethodId(JNIEnv
* env
,
136 const char* method_name
,
137 const char* method_sig
,
138 jmethodID
* method_id
) {
139 *method_id
= env
->GetStaticMethodID(clazz
, method_name
, method_sig
);
141 LOG_ERROR("Could not find ID for static method '%s'", method_name
);
144 LOG_INFO("%s: Found ID %p for static method '%s'",
145 __FUNCTION__
, *method_id
, method_name
);
149 // Initialize a jfieldID corresponding to the static field of a given |clazz|,
150 // with name |field_name| and signature |field_sig|.
151 // |env| is the current JNI environment handle.
152 // On success, return true and set |*field_id|.
153 bool InitStaticFieldId(JNIEnv
* env
,
155 const char* field_name
,
156 const char* field_sig
,
157 jfieldID
* field_id
) {
158 *field_id
= env
->GetStaticFieldID(clazz
, field_name
, field_sig
);
160 LOG_ERROR("Could not find ID for static field '%s'", field_name
);
164 "%s: Found ID %p for static field '%s'",
165 __FUNCTION__
, *field_id
, field_name
);
169 // Initialize a jint corresponding to the static integer field of a class
170 // with class name |class_name| and field name |field_name|.
171 // |env| is the current JNI environment handle.
172 // On success, return true and set |*value|.
173 bool InitStaticInt(JNIEnv
* env
,
174 const char* class_name
,
175 const char* field_name
,
178 if (!InitClassReference(env
, class_name
, &clazz
))
182 if (!InitStaticFieldId(env
, clazz
, field_name
, "I", &field_id
))
185 *value
= env
->GetStaticIntField(clazz
, field_id
);
187 "%s: Found value %d for class '%s', static field '%s'",
188 __FUNCTION__
, *value
, class_name
, field_name
);
193 // A class used to model the field IDs of the org.chromium.base.Linker
194 // LibInfo inner class, used to communicate data with the Java side
196 struct LibInfo_class
{
197 jfieldID load_address_id
;
198 jfieldID load_size_id
;
199 jfieldID relro_start_id
;
200 jfieldID relro_size_id
;
201 jfieldID relro_fd_id
;
203 // Initialize an instance.
204 bool Init(JNIEnv
* env
) {
206 if (!InitClassReference(
207 env
, "org/chromium/base/library_loader/Linker$LibInfo", &clazz
)) {
211 return InitFieldId(env
, clazz
, "mLoadAddress", "J", &load_address_id
) &&
212 InitFieldId(env
, clazz
, "mLoadSize", "J", &load_size_id
) &&
213 InitFieldId(env
, clazz
, "mRelroStart", "J", &relro_start_id
) &&
214 InitFieldId(env
, clazz
, "mRelroSize", "J", &relro_size_id
) &&
215 InitFieldId(env
, clazz
, "mRelroFd", "I", &relro_fd_id
);
218 void SetLoadInfo(JNIEnv
* env
,
219 jobject library_info_obj
,
222 env
->SetLongField(library_info_obj
, load_address_id
, load_address
);
223 env
->SetLongField(library_info_obj
, load_size_id
, load_size
);
226 // Use this instance to convert a RelroInfo reference into
227 // a crazy_library_info_t.
228 void GetRelroInfo(JNIEnv
* env
,
229 jobject library_info_obj
,
233 *relro_start
= static_cast<size_t>(
234 env
->GetLongField(library_info_obj
, relro_start_id
));
237 static_cast<size_t>(env
->GetLongField(library_info_obj
, relro_size_id
));
239 *relro_fd
= env
->GetIntField(library_info_obj
, relro_fd_id
);
242 void SetRelroInfo(JNIEnv
* env
,
243 jobject library_info_obj
,
247 env
->SetLongField(library_info_obj
, relro_start_id
, relro_start
);
248 env
->SetLongField(library_info_obj
, relro_size_id
, relro_size
);
249 env
->SetIntField(library_info_obj
, relro_fd_id
, relro_fd
);
253 static LibInfo_class s_lib_info_fields
;
255 // Retrieve the SDK build version and pass it into the crazy linker. This
256 // needs to be done early in initialization, before any other crazy linker
258 // |env| is the current JNI environment handle.
259 // On success, return true.
260 bool InitSDKVersionInfo(JNIEnv
* env
) {
262 if (!InitStaticInt(env
, "android/os/Build$VERSION", "SDK_INT", &value
))
265 crazy_set_sdk_build_version(static_cast<int>(value
));
266 LOG_INFO("%s: Set SDK build version to %d",
267 __FUNCTION__
, static_cast<int>(value
));
272 // The linker uses a single crazy_context_t object created on demand.
273 // There is no need to protect this against concurrent access, locking
274 // is already handled on the Java side.
275 static crazy_context_t
* s_crazy_context
;
277 crazy_context_t
* GetCrazyContext() {
278 if (!s_crazy_context
) {
279 // Create new context.
280 s_crazy_context
= crazy_context_create();
282 // Ensure libraries located in the same directory as the linker
283 // can be loaded before system ones.
284 crazy_context_add_search_path_for_address(
285 s_crazy_context
, reinterpret_cast<void*>(&s_crazy_context
));
288 return s_crazy_context
;
291 // A scoped crazy_library_t that automatically closes the handle
292 // on scope exit, unless Release() has been called.
293 class ScopedLibrary
{
295 ScopedLibrary() : lib_(NULL
) {}
299 crazy_library_close_with_context(lib_
, GetCrazyContext());
302 crazy_library_t
* Get() { return lib_
; }
304 crazy_library_t
** GetPtr() { return &lib_
; }
306 crazy_library_t
* Release() {
307 crazy_library_t
* ret
= lib_
;
313 crazy_library_t
* lib_
;
318 template <class LibraryOpener
>
319 bool GenericLoadLibrary(
321 const char* library_name
, jlong load_address
, jobject lib_info_obj
,
322 const LibraryOpener
& opener
) {
323 crazy_context_t
* context
= GetCrazyContext();
325 if (!IsValidAddress(load_address
)) {
326 LOG_ERROR("%s: Invalid address 0x%llx", __FUNCTION__
, load_address
);
330 // Set the desired load address (0 means randomize it).
331 crazy_context_set_load_address(context
, static_cast<size_t>(load_address
));
333 ScopedLibrary library
;
334 if (!opener
.Open(library
.GetPtr(), library_name
, context
)) {
338 crazy_library_info_t info
;
339 if (!crazy_library_get_info(library
.Get(), context
, &info
)) {
340 LOG_ERROR("%s: Could not get library information for %s: %s",
343 crazy_context_get_error(context
));
347 // Release library object to keep it alive after the function returns.
350 s_lib_info_fields
.SetLoadInfo(
351 env
, lib_info_obj
, info
.load_address
, info
.load_size
);
352 LOG_INFO("%s: Success loading library %s", __FUNCTION__
, library_name
);
356 // Used for opening the library in a regular file.
357 class FileLibraryOpener
{
360 crazy_library_t
** library
,
361 const char* library_name
,
362 crazy_context_t
* context
) const;
365 bool FileLibraryOpener::Open(
366 crazy_library_t
** library
,
367 const char* library_name
,
368 crazy_context_t
* context
) const {
369 if (!crazy_library_open(library
, library_name
, context
)) {
370 LOG_ERROR("%s: Could not open %s: %s",
373 crazy_context_get_error(context
));
379 // Used for opening the library in a zip file.
380 class ZipLibraryOpener
{
382 explicit ZipLibraryOpener(const char* zip_file
) : zip_file_(zip_file
) {}
384 crazy_library_t
** library
,
385 const char* library_name
,
386 crazy_context_t
* context
) const;
388 const char* zip_file_
;
391 bool ZipLibraryOpener::Open(
392 crazy_library_t
** library
,
393 const char* library_name
,
394 crazy_context_t
* context
) const {
395 if (!crazy_library_open_in_zip_file(
396 library
, zip_file_
, library_name
, context
)) {
397 LOG_ERROR("%s: Could not open %s in zip file %s: %s",
398 __FUNCTION__
, library_name
, zip_file_
,
399 crazy_context_get_error(context
));
405 } // unnamed namespace
407 // Load a library with the chromium linker. This will also call its
408 // JNI_OnLoad() method, which shall register its methods. Note that
409 // lazy native method resolution will _not_ work after this, because
410 // Dalvik uses the system's dlsym() which won't see the new library,
411 // so explicit registration is mandatory.
412 // |env| is the current JNI environment handle.
413 // |clazz| is the static class handle for org.chromium.base.Linker,
414 // and is ignored here.
415 // |library_name| is the library name (e.g. libfoo.so).
416 // |load_address| is an explicit load address.
417 // |library_info| is a LibInfo handle used to communicate information
418 // with the Java side.
419 // Return true on success.
420 jboolean
LoadLibrary(JNIEnv
* env
,
422 jstring library_name
,
424 jobject lib_info_obj
) {
425 String
lib_name(env
, library_name
);
426 FileLibraryOpener opener
;
427 return GenericLoadLibrary(
428 env
, lib_name
.c_str(),
429 static_cast<size_t>(load_address
), lib_info_obj
, opener
);
432 // Load a library from a zipfile with the chromium linker. The
433 // library in the zipfile must be uncompressed and page aligned.
434 // The basename of the library is given. The library is expected
435 // to be lib/<abi_tag>/crazy.<basename>. The <abi_tag> used will be the
436 // same as the abi for this linker. The "crazy." prefix is included
437 // so that the Android Package Manager doesn't extract the library into
440 // Loading the library will also call its JNI_OnLoad() method, which
441 // shall register its methods. Note that lazy native method resolution
442 // will _not_ work after this, because Dalvik uses the system's dlsym()
443 // which won't see the new library, so explicit registration is mandatory.
445 // |env| is the current JNI environment handle.
446 // |clazz| is the static class handle for org.chromium.base.Linker,
447 // and is ignored here.
448 // |zipfile_name| is the filename of the zipfile containing the library.
449 // |library_name| is the library base name (e.g. libfoo.so).
450 // |load_address| is an explicit load address.
451 // |library_info| is a LibInfo handle used to communicate information
452 // with the Java side.
453 // Returns true on success.
454 jboolean
LoadLibraryInZipFile(JNIEnv
* env
,
456 jstring zipfile_name
,
457 jstring library_name
,
459 jobject lib_info_obj
) {
460 String
zipfile_name_str(env
, zipfile_name
);
461 String
lib_name(env
, library_name
);
462 ZipLibraryOpener
opener(zipfile_name_str
.c_str());
463 return GenericLoadLibrary(
464 env
, lib_name
.c_str(),
465 static_cast<size_t>(load_address
), lib_info_obj
, opener
);
468 // Class holding the Java class and method ID for the Java side Linker
469 // postCallbackOnMainThread method.
470 struct JavaCallbackBindings_class
{
474 // Initialize an instance.
475 bool Init(JNIEnv
* env
, jclass linker_class
) {
476 clazz
= reinterpret_cast<jclass
>(env
->NewGlobalRef(linker_class
));
477 return InitStaticMethodId(env
,
479 "postCallbackOnMainThread",
485 static JavaCallbackBindings_class s_java_callback_bindings
;
487 // Designated receiver function for callbacks from Java. Its name is known
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 void RunCallbackOnUiThread(JNIEnv
* env
, jclass clazz
, jlong arg
) {
494 crazy_callback_t
* callback
= reinterpret_cast<crazy_callback_t
*>(arg
);
496 LOG_INFO("%s: Called back from java with handler %p, opaque %p",
497 __FUNCTION__
, callback
->handler
, callback
->opaque
);
499 crazy_callback_run(callback
);
503 // Request a callback from Java. The supplied crazy_callback_t is valid only
504 // for the duration of this call, so we copy it to a newly allocated
505 // crazy_callback_t and then call the Java side's postCallbackOnMainThread.
506 // This will call back to to our RunCallbackOnUiThread some time
507 // later on the UI thread.
508 // |callback_request| is a crazy_callback_t.
509 // |poster_opaque| is unused.
510 // Returns true if the callback request succeeds.
511 static bool PostForLaterExecution(crazy_callback_t
* callback_request
,
512 void* poster_opaque UNUSED
) {
513 crazy_context_t
* context
= GetCrazyContext();
516 int minimum_jni_version
;
517 crazy_context_get_java_vm(context
,
518 reinterpret_cast<void**>(&vm
),
519 &minimum_jni_version
);
521 // Do not reuse JNIEnv from JNI_OnLoad, but retrieve our own.
523 if (JNI_OK
!= vm
->GetEnv(
524 reinterpret_cast<void**>(&env
), minimum_jni_version
)) {
525 LOG_ERROR("Could not create JNIEnv");
529 // Copy the callback; the one passed as an argument may be temporary.
530 crazy_callback_t
* callback
= new crazy_callback_t();
531 *callback
= *callback_request
;
533 LOG_INFO("%s: Calling back to java with handler %p, opaque %p",
534 __FUNCTION__
, callback
->handler
, callback
->opaque
);
536 jlong arg
= static_cast<jlong
>(reinterpret_cast<uintptr_t>(callback
));
538 env
->CallStaticVoidMethod(
539 s_java_callback_bindings
.clazz
, s_java_callback_bindings
.method_id
, arg
);
541 // Back out and return false if we encounter a JNI exception.
542 if (env
->ExceptionCheck() == JNI_TRUE
) {
543 env
->ExceptionDescribe();
544 env
->ExceptionClear();
552 jboolean
CreateSharedRelro(JNIEnv
* env
,
554 jstring library_name
,
556 jobject lib_info_obj
) {
557 String
lib_name(env
, library_name
);
559 LOG_INFO("%s: Called for %s", __FUNCTION__
, lib_name
.c_str());
561 if (!IsValidAddress(load_address
)) {
562 LOG_ERROR("%s: Invalid address 0x%llx", __FUNCTION__
, load_address
);
566 ScopedLibrary library
;
567 if (!crazy_library_find_by_name(lib_name
.c_str(), library
.GetPtr())) {
568 LOG_ERROR("%s: Could not find %s", __FUNCTION__
, lib_name
.c_str());
572 crazy_context_t
* context
= GetCrazyContext();
573 size_t relro_start
= 0;
574 size_t relro_size
= 0;
577 if (!crazy_library_create_shared_relro(library
.Get(),
579 static_cast<size_t>(load_address
),
583 LOG_ERROR("%s: Could not create shared RELRO sharing for %s: %s\n",
586 crazy_context_get_error(context
));
590 s_lib_info_fields
.SetRelroInfo(
591 env
, lib_info_obj
, relro_start
, relro_size
, relro_fd
);
595 jboolean
UseSharedRelro(JNIEnv
* env
,
597 jstring library_name
,
598 jobject lib_info_obj
) {
599 String
lib_name(env
, library_name
);
601 LOG_INFO("%s: called for %s, lib_info_ref=%p",
606 ScopedLibrary library
;
607 if (!crazy_library_find_by_name(lib_name
.c_str(), library
.GetPtr())) {
608 LOG_ERROR("%s: Could not find %s", __FUNCTION__
, lib_name
.c_str());
612 crazy_context_t
* context
= GetCrazyContext();
613 size_t relro_start
= 0;
614 size_t relro_size
= 0;
616 s_lib_info_fields
.GetRelroInfo(
617 env
, lib_info_obj
, &relro_start
, &relro_size
, &relro_fd
);
619 LOG_INFO("%s: library=%s relro start=%p size=%p fd=%d",
626 if (!crazy_library_use_shared_relro(
627 library
.Get(), context
, relro_start
, relro_size
, relro_fd
)) {
628 LOG_ERROR("%s: Could not use shared RELRO for %s: %s",
631 crazy_context_get_error(context
));
635 LOG_INFO("%s: Library %s using shared RELRO section!",
642 jlong
GetRandomBaseLoadAddress(JNIEnv
* env
, jclass clazz
, jlong bytes
) {
643 #if RESERVE_BREAKPAD_GUARD_REGION
644 // Add a Breakpad guard region. 16Mb should be comfortably larger than
645 // the largest relocation packer saving we expect to encounter.
646 static const size_t kBreakpadGuardRegionBytes
= 16 * 1024 * 1024;
647 bytes
+= kBreakpadGuardRegionBytes
;
651 mmap(NULL
, bytes
, PROT_NONE
, MAP_PRIVATE
| MAP_ANONYMOUS
, -1, 0);
652 if (address
== MAP_FAILED
) {
653 LOG_INFO("%s: Random base load address not determinable\n", __FUNCTION__
);
656 munmap(address
, bytes
);
658 #if RESERVE_BREAKPAD_GUARD_REGION
659 // Allow for a Breakpad guard region ahead of the returned address.
660 address
= reinterpret_cast<void*>(
661 reinterpret_cast<uintptr_t>(address
) + kBreakpadGuardRegionBytes
);
664 LOG_INFO("%s: Random base load address is %p\n", __FUNCTION__
, address
);
665 return static_cast<jlong
>(reinterpret_cast<uintptr_t>(address
));
668 const JNINativeMethod kNativeMethods
[] = {
669 {"nativeLoadLibrary",
673 "Lorg/chromium/base/library_loader/Linker$LibInfo;"
676 reinterpret_cast<void*>(&LoadLibrary
)},
677 {"nativeLoadLibraryInZipFile",
682 "Lorg/chromium/base/library_loader/Linker$LibInfo;"
685 reinterpret_cast<void*>(&LoadLibraryInZipFile
)},
686 {"nativeRunCallbackOnUiThread",
691 reinterpret_cast<void*>(&RunCallbackOnUiThread
)},
692 {"nativeCreateSharedRelro",
696 "Lorg/chromium/base/library_loader/Linker$LibInfo;"
699 reinterpret_cast<void*>(&CreateSharedRelro
)},
700 {"nativeUseSharedRelro",
703 "Lorg/chromium/base/library_loader/Linker$LibInfo;"
706 reinterpret_cast<void*>(&UseSharedRelro
)},
707 {"nativeGetRandomBaseLoadAddress",
712 reinterpret_cast<void*>(&GetRandomBaseLoadAddress
)},
717 // JNI_OnLoad() hook called when the linker library is loaded through
718 // the regular System.LoadLibrary) API. This shall save the Java VM
719 // handle and initialize LibInfo fields.
720 jint
JNI_OnLoad(JavaVM
* vm
, void* reserved
) {
721 LOG_INFO("%s: Entering", __FUNCTION__
);
724 if (JNI_OK
!= vm
->GetEnv(reinterpret_cast<void**>(&env
), JNI_VERSION_1_4
)) {
725 LOG_ERROR("Could not create JNIEnv");
729 // Initialize SDK version info.
730 LOG_INFO("%s: Retrieving SDK version info", __FUNCTION__
);
731 if (!InitSDKVersionInfo(env
))
734 // Register native methods.
736 if (!InitClassReference(env
,
737 "org/chromium/base/library_loader/LegacyLinker",
741 LOG_INFO("%s: Registering native methods", __FUNCTION__
);
742 env
->RegisterNatives(linker_class
,
744 sizeof(kNativeMethods
) / sizeof(kNativeMethods
[0]));
746 // Find LibInfo field ids.
747 LOG_INFO("%s: Caching field IDs", __FUNCTION__
);
748 if (!s_lib_info_fields
.Init(env
)) {
752 // Resolve and save the Java side Linker callback class and method.
753 LOG_INFO("%s: Resolving callback bindings", __FUNCTION__
);
754 if (!s_java_callback_bindings
.Init(env
, linker_class
)) {
758 // Save JavaVM* handle into context.
759 crazy_context_t
* context
= GetCrazyContext();
760 crazy_context_set_java_vm(context
, vm
, JNI_VERSION_1_4
);
762 // Register the function that the crazy linker can call to post code
763 // for later execution.
764 crazy_context_set_callback_poster(context
, &PostForLaterExecution
, NULL
);
766 LOG_INFO("%s: Done", __FUNCTION__
);
767 return JNI_VERSION_1_4
;