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