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. 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 (e.g. libcontentshell.so). 8 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 11 // renderer process. 12 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. 15 16 #include <android/log.h> 17 #include <crazy_linker.h> 18 #include <jni.h> 19 #include <stdlib.h> 20 #include <sys/mman.h> 21 #include <unistd.h> 22 23 // Set this to 1 to enable debug traces to the Android log. 24 // Note that LOG() from "base/logging.h" cannot be used, since it is 25 // in base/ which hasn't been loaded yet. 26 #define DEBUG 0 27 28 #define TAG "chromium_android_linker" 29 30 #if DEBUG 31 #define LOG_INFO(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__) 32 #else 33 #define LOG_INFO(...) ((void)0) 34 #endif 35 #define LOG_ERROR(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__) 36 37 #define UNUSED __attribute__((unused)) 38 39 namespace { 40 41 // A simply scoped UTF String class that can be initialized from 42 // a Java jstring handle. Modeled like std::string, which cannot 43 // be used here. 44 class String { 45 public: 46 String(JNIEnv* env, jstring str); 47 48 ~String() { 49 if (ptr_) 50 ::free(ptr_); 51 } 52 53 const char* c_str() const { return ptr_ ? ptr_ : ""; } 54 size_t size() const { return size_; } 55 56 private: 57 char* ptr_; 58 size_t size_; 59 }; 60 61 String::String(JNIEnv* env, jstring str) { 62 size_ = env->GetStringUTFLength(str); 63 ptr_ = static_cast<char*>(::malloc(size_ + 1)); 64 65 // Note: the result contains Java "modified UTF-8" bytes. 66 // Good enough for the linker though. 67 const char* bytes = env->GetStringUTFChars(str, NULL); 68 ::memcpy(ptr_, bytes, size_); 69 ptr_[size_] = '\0'; 70 71 env->ReleaseStringUTFChars(str, bytes); 72 } 73 74 // Return true iff |address| is a valid address for the target CPU. 75 bool IsValidAddress(jlong address) { 76 return static_cast<jlong>(static_cast<size_t>(address)) == address; 77 } 78 79 // Find the jclass JNI reference corresponding to a given |class_name|. 80 // |env| is the current JNI environment handle. 81 // On success, return true and set |*clazz|. 82 bool InitClassReference(JNIEnv* env, const char* class_name, jclass* clazz) { 83 *clazz = env->FindClass(class_name); 84 if (!*clazz) { 85 LOG_ERROR("Could not find class for %s", class_name); 86 return false; 87 } 88 return true; 89 } 90 91 // Initialize a jfieldID corresponding to the field of a given |clazz|, 92 // with name |field_name| and signature |field_sig|. 93 // |env| is the current JNI environment handle. 94 // On success, return true and set |*field_id|. 95 bool InitFieldId(JNIEnv* env, 96 jclass clazz, 97 const char* field_name, 98 const char* field_sig, 99 jfieldID* field_id) { 100 *field_id = env->GetFieldID(clazz, field_name, field_sig); 101 if (!*field_id) { 102 LOG_ERROR("Could not find ID for field '%s'", field_name); 103 return false; 104 } 105 LOG_INFO( 106 "%s: Found ID %p for field '%s'", __FUNCTION__, *field_id, field_name); 107 return true; 108 } 109 110 // Initialize a jmethodID corresponding to the static method of a given 111 // |clazz|, with name |method_name| and signature |method_sig|. 112 // |env| is the current JNI environment handle. 113 // On success, return true and set |*method_id|. 114 bool InitStaticMethodId(JNIEnv* env, 115 jclass clazz, 116 const char* method_name, 117 const char* method_sig, 118 jmethodID* method_id) { 119 *method_id = env->GetStaticMethodID(clazz, method_name, method_sig); 120 if (!*method_id) { 121 LOG_ERROR("Could not find ID for static method '%s'", method_name); 122 return false; 123 } 124 LOG_INFO("%s: Found ID %p for static method '%s'", 125 __FUNCTION__, *method_id, method_name); 126 return true; 127 } 128 129 // A class used to model the field IDs of the org.chromium.base.Linker 130 // LibInfo inner class, used to communicate data with the Java side 131 // of the linker. 132 struct LibInfo_class { 133 jfieldID load_address_id; 134 jfieldID load_size_id; 135 jfieldID relro_start_id; 136 jfieldID relro_size_id; 137 jfieldID relro_fd_id; 138 139 // Initialize an instance. 140 bool Init(JNIEnv* env) { 141 jclass clazz; 142 if (!InitClassReference( 143 env, "org/chromium/base/library_loader/Linker$LibInfo", &clazz)) { 144 return false; 145 } 146 147 return InitFieldId(env, clazz, "mLoadAddress", "J", &load_address_id) && 148 InitFieldId(env, clazz, "mLoadSize", "J", &load_size_id) && 149 InitFieldId(env, clazz, "mRelroStart", "J", &relro_start_id) && 150 InitFieldId(env, clazz, "mRelroSize", "J", &relro_size_id) && 151 InitFieldId(env, clazz, "mRelroFd", "I", &relro_fd_id); 152 } 153 154 void SetLoadInfo(JNIEnv* env, 155 jobject library_info_obj, 156 size_t load_address, 157 size_t load_size) { 158 env->SetLongField(library_info_obj, load_address_id, load_address); 159 env->SetLongField(library_info_obj, load_size_id, load_size); 160 } 161 162 // Use this instance to convert a RelroInfo reference into 163 // a crazy_library_info_t. 164 void GetRelroInfo(JNIEnv* env, 165 jobject library_info_obj, 166 size_t* relro_start, 167 size_t* relro_size, 168 int* relro_fd) { 169 *relro_start = static_cast<size_t>( 170 env->GetLongField(library_info_obj, relro_start_id)); 171 172 *relro_size = 173 static_cast<size_t>(env->GetLongField(library_info_obj, relro_size_id)); 174 175 *relro_fd = env->GetIntField(library_info_obj, relro_fd_id); 176 } 177 178 void SetRelroInfo(JNIEnv* env, 179 jobject library_info_obj, 180 size_t relro_start, 181 size_t relro_size, 182 int relro_fd) { 183 env->SetLongField(library_info_obj, relro_start_id, relro_start); 184 env->SetLongField(library_info_obj, relro_size_id, relro_size); 185 env->SetIntField(library_info_obj, relro_fd_id, relro_fd); 186 } 187 }; 188 189 static LibInfo_class s_lib_info_fields; 190 191 // The linker uses a single crazy_context_t object created on demand. 192 // There is no need to protect this against concurrent access, locking 193 // is already handled on the Java side. 194 static crazy_context_t* s_crazy_context; 195 196 crazy_context_t* GetCrazyContext() { 197 if (!s_crazy_context) { 198 // Create new context. 199 s_crazy_context = crazy_context_create(); 200 201 // Ensure libraries located in the same directory as the linker 202 // can be loaded before system ones. 203 crazy_context_add_search_path_for_address( 204 s_crazy_context, reinterpret_cast<void*>(&s_crazy_context)); 205 } 206 207 return s_crazy_context; 208 } 209 210 // A scoped crazy_library_t that automatically closes the handle 211 // on scope exit, unless Release() has been called. 212 class ScopedLibrary { 213 public: 214 ScopedLibrary() : lib_(NULL) {} 215 216 ~ScopedLibrary() { 217 if (lib_) 218 crazy_library_close_with_context(lib_, GetCrazyContext()); 219 } 220 221 crazy_library_t* Get() { return lib_; } 222 223 crazy_library_t** GetPtr() { return &lib_; } 224 225 crazy_library_t* Release() { 226 crazy_library_t* ret = lib_; 227 lib_ = NULL; 228 return ret; 229 } 230 231 private: 232 crazy_library_t* lib_; 233 }; 234 235 namespace { 236 237 template <class LibraryOpener> 238 bool GenericLoadLibrary( 239 JNIEnv* env, 240 const char* library_name, jlong load_address, jobject lib_info_obj, 241 const LibraryOpener& opener) { 242 crazy_context_t* context = GetCrazyContext(); 243 244 if (!IsValidAddress(load_address)) { 245 LOG_ERROR("%s: Invalid address 0x%llx", __FUNCTION__, load_address); 246 return false; 247 } 248 249 // Set the desired load address (0 means randomize it). 250 crazy_context_set_load_address(context, static_cast<size_t>(load_address)); 251 252 ScopedLibrary library; 253 if (!opener.Open(library.GetPtr(), library_name, context)) { 254 return false; 255 } 256 257 crazy_library_info_t info; 258 if (!crazy_library_get_info(library.Get(), context, &info)) { 259 LOG_ERROR("%s: Could not get library information for %s: %s", 260 __FUNCTION__, 261 library_name, 262 crazy_context_get_error(context)); 263 return false; 264 } 265 266 // Release library object to keep it alive after the function returns. 267 library.Release(); 268 269 s_lib_info_fields.SetLoadInfo( 270 env, lib_info_obj, info.load_address, info.load_size); 271 LOG_INFO("%s: Success loading library %s", __FUNCTION__, library_name); 272 return true; 273 } 274 275 // Used for opening the library in a regular file. 276 class FileLibraryOpener { 277 public: 278 bool Open( 279 crazy_library_t** library, 280 const char* library_name, 281 crazy_context_t* context) const; 282 }; 283 284 bool FileLibraryOpener::Open( 285 crazy_library_t** library, 286 const char* library_name, 287 crazy_context_t* context) const { 288 if (!crazy_library_open(library, library_name, context)) { 289 LOG_ERROR("%s: Could not open %s: %s", 290 __FUNCTION__, 291 library_name, 292 crazy_context_get_error(context)); 293 return false; 294 } 295 return true; 296 } 297 298 // Used for opening the library in a zip file. 299 class ZipLibraryOpener { 300 public: 301 explicit ZipLibraryOpener(const char* zip_file) : zip_file_(zip_file) {} 302 bool Open( 303 crazy_library_t** library, 304 const char* library_name, 305 crazy_context_t* context) const; 306 private: 307 const char* zip_file_; 308 }; 309 310 bool ZipLibraryOpener::Open( 311 crazy_library_t** library, 312 const char* library_name, 313 crazy_context_t* context) const { 314 if (!crazy_library_open_in_zip_file( 315 library, zip_file_, library_name, context)) { 316 LOG_ERROR("%s: Could not open %s in zip file %s: %s", 317 __FUNCTION__, library_name, zip_file_, 318 crazy_context_get_error(context)); 319 return false; 320 } 321 return true; 322 } 323 324 } // unnamed namespace 325 326 // Load a library with the chromium linker. This will also call its 327 // JNI_OnLoad() method, which shall register its methods. Note that 328 // lazy native method resolution will _not_ work after this, because 329 // Dalvik uses the system's dlsym() which won't see the new library, 330 // so explicit registration is mandatory. 331 // |env| is the current JNI environment handle. 332 // |clazz| is the static class handle for org.chromium.base.Linker, 333 // and is ignored here. 334 // |library_name| is the library name (e.g. libfoo.so). 335 // |load_address| is an explicit load address. 336 // |library_info| is a LibInfo handle used to communicate information 337 // with the Java side. 338 // Return true on success. 339 jboolean LoadLibrary(JNIEnv* env, 340 jclass clazz, 341 jstring library_name, 342 jlong load_address, 343 jobject lib_info_obj) { 344 String lib_name(env, library_name); 345 FileLibraryOpener opener; 346 return GenericLoadLibrary( 347 env, lib_name.c_str(), 348 static_cast<size_t>(load_address), lib_info_obj, opener); 349 } 350 351 // Load a library from a zipfile with the chromium linker. The 352 // library in the zipfile must be uncompressed and page aligned. 353 // The basename of the library is given. The library is expected 354 // to be lib/<abi_tag>/crazy.<basename>. The <abi_tag> used will be the 355 // same as the abi for this linker. The "crazy." prefix is included 356 // so that the Android Package Manager doesn't extract the library into 357 // /data/app-lib. 358 // 359 // Loading the library will also call its JNI_OnLoad() method, which 360 // shall register its methods. Note that lazy native method resolution 361 // will _not_ work after this, because Dalvik uses the system's dlsym() 362 // which won't see the new library, so explicit registration is mandatory. 363 // 364 // |env| is the current JNI environment handle. 365 // |clazz| is the static class handle for org.chromium.base.Linker, 366 // and is ignored here. 367 // |zipfile_name| is the filename of the zipfile containing the library. 368 // |library_name| is the library base name (e.g. libfoo.so). 369 // |load_address| is an explicit load address. 370 // |library_info| is a LibInfo handle used to communicate information 371 // with the Java side. 372 // Returns true on success. 373 jboolean LoadLibraryInZipFile(JNIEnv* env, 374 jclass clazz, 375 jstring zipfile_name, 376 jstring library_name, 377 jlong load_address, 378 jobject lib_info_obj) { 379 String zipfile_name_str(env, zipfile_name); 380 String lib_name(env, library_name); 381 ZipLibraryOpener opener(zipfile_name_str.c_str()); 382 return GenericLoadLibrary( 383 env, lib_name.c_str(), 384 static_cast<size_t>(load_address), lib_info_obj, opener); 385 } 386 387 // Class holding the Java class and method ID for the Java side Linker 388 // postCallbackOnMainThread method. 389 struct JavaCallbackBindings_class { 390 jclass clazz; 391 jmethodID method_id; 392 393 // Initialize an instance. 394 bool Init(JNIEnv* env, jclass linker_class) { 395 clazz = reinterpret_cast<jclass>(env->NewGlobalRef(linker_class)); 396 return InitStaticMethodId(env, 397 linker_class, 398 "postCallbackOnMainThread", 399 "(J)V", 400 &method_id); 401 } 402 }; 403 404 static JavaCallbackBindings_class s_java_callback_bindings; 405 406 // Designated receiver function for callbacks from Java. Its name is known 407 // to the Java side. 408 // |env| is the current JNI environment handle and is ignored here. 409 // |clazz| is the static class handle for org.chromium.base.Linker, 410 // and is ignored here. 411 // |arg| is a pointer to an allocated crazy_callback_t, deleted after use. 412 void RunCallbackOnUiThread(JNIEnv* env, jclass clazz, jlong arg) { 413 crazy_callback_t* callback = reinterpret_cast<crazy_callback_t*>(arg); 414 415 LOG_INFO("%s: Called back from java with handler %p, opaque %p", 416 __FUNCTION__, callback->handler, callback->opaque); 417 418 crazy_callback_run(callback); 419 delete callback; 420 } 421 422 // Request a callback from Java. The supplied crazy_callback_t is valid only 423 // for the duration of this call, so we copy it to a newly allocated 424 // crazy_callback_t and then call the Java side's postCallbackOnMainThread. 425 // This will call back to to our RunCallbackOnUiThread some time 426 // later on the UI thread. 427 // |callback_request| is a crazy_callback_t. 428 // |poster_opaque| is unused. 429 // Returns true if the callback request succeeds. 430 static bool PostForLaterExecution(crazy_callback_t* callback_request, 431 void* poster_opaque UNUSED) { 432 crazy_context_t* context = GetCrazyContext(); 433 434 JavaVM* vm; 435 int minimum_jni_version; 436 crazy_context_get_java_vm(context, 437 reinterpret_cast<void**>(&vm), 438 &minimum_jni_version); 439 440 // Do not reuse JNIEnv from JNI_OnLoad, but retrieve our own. 441 JNIEnv* env; 442 if (JNI_OK != vm->GetEnv( 443 reinterpret_cast<void**>(&env), minimum_jni_version)) { 444 LOG_ERROR("Could not create JNIEnv"); 445 return false; 446 } 447 448 // Copy the callback; the one passed as an argument may be temporary. 449 crazy_callback_t* callback = new crazy_callback_t(); 450 *callback = *callback_request; 451 452 LOG_INFO("%s: Calling back to java with handler %p, opaque %p", 453 __FUNCTION__, callback->handler, callback->opaque); 454 455 jlong arg = static_cast<jlong>(reinterpret_cast<uintptr_t>(callback)); 456 457 env->CallStaticVoidMethod( 458 s_java_callback_bindings.clazz, s_java_callback_bindings.method_id, arg); 459 460 // Back out and return false if we encounter a JNI exception. 461 if (env->ExceptionCheck() == JNI_TRUE) { 462 env->ExceptionDescribe(); 463 env->ExceptionClear(); 464 delete callback; 465 return false; 466 } 467 468 return true; 469 } 470 471 jboolean CreateSharedRelro(JNIEnv* env, 472 jclass clazz, 473 jstring library_name, 474 jlong load_address, 475 jobject lib_info_obj) { 476 String lib_name(env, library_name); 477 478 LOG_INFO("%s: Called for %s", __FUNCTION__, lib_name.c_str()); 479 480 if (!IsValidAddress(load_address)) { 481 LOG_ERROR("%s: Invalid address 0x%llx", __FUNCTION__, load_address); 482 return false; 483 } 484 485 ScopedLibrary library; 486 if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) { 487 LOG_ERROR("%s: Could not find %s", __FUNCTION__, lib_name.c_str()); 488 return false; 489 } 490 491 crazy_context_t* context = GetCrazyContext(); 492 size_t relro_start = 0; 493 size_t relro_size = 0; 494 int relro_fd = -1; 495 496 if (!crazy_library_create_shared_relro(library.Get(), 497 context, 498 static_cast<size_t>(load_address), 499 &relro_start, 500 &relro_size, 501 &relro_fd)) { 502 LOG_ERROR("%s: Could not create shared RELRO sharing for %s: %s\n", 503 __FUNCTION__, 504 lib_name.c_str(), 505 crazy_context_get_error(context)); 506 return false; 507 } 508 509 s_lib_info_fields.SetRelroInfo( 510 env, lib_info_obj, relro_start, relro_size, relro_fd); 511 return true; 512 } 513 514 jboolean UseSharedRelro(JNIEnv* env, 515 jclass clazz, 516 jstring library_name, 517 jobject lib_info_obj) { 518 String lib_name(env, library_name); 519 520 LOG_INFO("%s: called for %s, lib_info_ref=%p", 521 __FUNCTION__, 522 lib_name.c_str(), 523 lib_info_obj); 524 525 ScopedLibrary library; 526 if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) { 527 LOG_ERROR("%s: Could not find %s", __FUNCTION__, lib_name.c_str()); 528 return false; 529 } 530 531 crazy_context_t* context = GetCrazyContext(); 532 size_t relro_start = 0; 533 size_t relro_size = 0; 534 int relro_fd = -1; 535 s_lib_info_fields.GetRelroInfo( 536 env, lib_info_obj, &relro_start, &relro_size, &relro_fd); 537 538 LOG_INFO("%s: library=%s relro start=%p size=%p fd=%d", 539 __FUNCTION__, 540 lib_name.c_str(), 541 (void*)relro_start, 542 (void*)relro_size, 543 relro_fd); 544 545 if (!crazy_library_use_shared_relro( 546 library.Get(), context, relro_start, relro_size, relro_fd)) { 547 LOG_ERROR("%s: Could not use shared RELRO for %s: %s", 548 __FUNCTION__, 549 lib_name.c_str(), 550 crazy_context_get_error(context)); 551 return false; 552 } 553 554 LOG_INFO("%s: Library %s using shared RELRO section!", 555 __FUNCTION__, 556 lib_name.c_str()); 557 558 return true; 559 } 560 561 jboolean CanUseSharedRelro(JNIEnv* env, jclass clazz) { 562 return crazy_system_can_share_relro(); 563 } 564 565 jlong GetRandomBaseLoadAddress(JNIEnv* env, jclass clazz, jlong bytes) { 566 void* address = 567 mmap(NULL, bytes, PROT_NONE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 568 if (address == MAP_FAILED) { 569 LOG_INFO("%s: Random base load address not determinable\n", __FUNCTION__); 570 return 0; 571 } 572 munmap(address, bytes); 573 LOG_INFO("%s: Random base load address is %p\n", __FUNCTION__, address); 574 return static_cast<jlong>(reinterpret_cast<uintptr_t>(address)); 575 } 576 577 const JNINativeMethod kNativeMethods[] = { 578 {"nativeLoadLibrary", 579 "(" 580 "Ljava/lang/String;" 581 "J" 582 "Lorg/chromium/base/library_loader/Linker$LibInfo;" 583 ")" 584 "Z", 585 reinterpret_cast<void*>(&LoadLibrary)}, 586 {"nativeLoadLibraryInZipFile", 587 "(" 588 "Ljava/lang/String;" 589 "Ljava/lang/String;" 590 "J" 591 "Lorg/chromium/base/library_loader/Linker$LibInfo;" 592 ")" 593 "Z", 594 reinterpret_cast<void*>(&LoadLibraryInZipFile)}, 595 {"nativeRunCallbackOnUiThread", 596 "(" 597 "J" 598 ")" 599 "V", 600 reinterpret_cast<void*>(&RunCallbackOnUiThread)}, 601 {"nativeCreateSharedRelro", 602 "(" 603 "Ljava/lang/String;" 604 "J" 605 "Lorg/chromium/base/library_loader/Linker$LibInfo;" 606 ")" 607 "Z", 608 reinterpret_cast<void*>(&CreateSharedRelro)}, 609 {"nativeUseSharedRelro", 610 "(" 611 "Ljava/lang/String;" 612 "Lorg/chromium/base/library_loader/Linker$LibInfo;" 613 ")" 614 "Z", 615 reinterpret_cast<void*>(&UseSharedRelro)}, 616 {"nativeCanUseSharedRelro", 617 "(" 618 ")" 619 "Z", 620 reinterpret_cast<void*>(&CanUseSharedRelro)}, 621 {"nativeGetRandomBaseLoadAddress", 622 "(" 623 "J" 624 ")" 625 "J", 626 reinterpret_cast<void*>(&GetRandomBaseLoadAddress)}, }; 627 628 } // namespace 629 630 // JNI_OnLoad() hook called when the linker library is loaded through 631 // the regular System.LoadLibrary) API. This shall save the Java VM 632 // handle and initialize LibInfo fields. 633 jint JNI_OnLoad(JavaVM* vm, void* reserved) { 634 LOG_INFO("%s: Entering", __FUNCTION__); 635 // Get new JNIEnv 636 JNIEnv* env; 637 if (JNI_OK != vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4)) { 638 LOG_ERROR("Could not create JNIEnv"); 639 return -1; 640 } 641 642 // Register native methods. 643 jclass linker_class; 644 if (!InitClassReference(env, 645 "org/chromium/base/library_loader/Linker", 646 &linker_class)) 647 return -1; 648 649 LOG_INFO("%s: Registering native methods", __FUNCTION__); 650 env->RegisterNatives(linker_class, 651 kNativeMethods, 652 sizeof(kNativeMethods) / sizeof(kNativeMethods[0])); 653 654 // Find LibInfo field ids. 655 LOG_INFO("%s: Caching field IDs", __FUNCTION__); 656 if (!s_lib_info_fields.Init(env)) { 657 return -1; 658 } 659 660 // Resolve and save the Java side Linker callback class and method. 661 LOG_INFO("%s: Resolving callback bindings", __FUNCTION__); 662 if (!s_java_callback_bindings.Init(env, linker_class)) { 663 return -1; 664 } 665 666 // Save JavaVM* handle into context. 667 crazy_context_t* context = GetCrazyContext(); 668 crazy_context_set_java_vm(context, vm, JNI_VERSION_1_4); 669 670 // Register the function that the crazy linker can call to post code 671 // for later execution. 672 crazy_context_set_callback_poster(context, &PostForLaterExecution, NULL); 673 674 LOG_INFO("%s: Done", __FUNCTION__); 675 return JNI_VERSION_1_4; 676 } 677