Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 // This is the Android-specific Chromium linker, a tiny shared library | 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 | 6 // implementing a custom dynamic linker that can be used to load the |
| 7 // real Chromium libraries (e.g. libcontentshell.so). | 7 // real Chromium libraries (e.g. libcontentshell.so). |
| 8 | 8 |
| 9 // The main point of this linker is to be able to share the RELRO | 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 | 10 // section of libcontentshell.so (or equivalent) between the browser and |
| (...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 64 | 64 |
| 65 // Note: the result contains Java "modified UTF-8" bytes. | 65 // Note: the result contains Java "modified UTF-8" bytes. |
| 66 // Good enough for the linker though. | 66 // Good enough for the linker though. |
| 67 const char* bytes = env->GetStringUTFChars(str, NULL); | 67 const char* bytes = env->GetStringUTFChars(str, NULL); |
| 68 ::memcpy(ptr_, bytes, size_); | 68 ::memcpy(ptr_, bytes, size_); |
| 69 ptr_[size_] = '\0'; | 69 ptr_[size_] = '\0'; |
| 70 | 70 |
| 71 env->ReleaseStringUTFChars(str, bytes); | 71 env->ReleaseStringUTFChars(str, bytes); |
| 72 } | 72 } |
| 73 | 73 |
| 74 // A scoped crazy_library_t that automatically closes the handle | |
| 75 // on scope exit, unless Release() has been called. | |
| 76 class ScopedLibrary { | |
| 77 public: | |
| 78 ScopedLibrary() : lib_(NULL) {} | |
| 79 | |
| 80 ~ScopedLibrary() { | |
| 81 if (lib_) | |
| 82 crazy_library_close(lib_); | |
| 83 } | |
| 84 | |
| 85 crazy_library_t* Get() { return lib_; } | |
| 86 | |
| 87 crazy_library_t** GetPtr() { return &lib_; } | |
| 88 | |
| 89 crazy_library_t* Release() { | |
| 90 crazy_library_t* ret = lib_; | |
| 91 lib_ = NULL; | |
| 92 return ret; | |
| 93 } | |
| 94 | |
| 95 private: | |
| 96 crazy_library_t* lib_; | |
| 97 }; | |
| 98 | |
| 99 // Return a pointer to the base name from an input |path| string. | 74 // Return a pointer to the base name from an input |path| string. |
| 100 const char* GetBaseNamePtr(const char* path) { | 75 const char* GetBaseNamePtr(const char* path) { |
| 101 const char* p = strrchr(path, '/'); | 76 const char* p = strrchr(path, '/'); |
| 102 if (p) | 77 if (p) |
| 103 return p + 1; | 78 return p + 1; |
| 104 return path; | 79 return path; |
| 105 } | 80 } |
| 106 | 81 |
| 107 // Return true iff |address| is a valid address for the target CPU. | 82 // Return true iff |address| is a valid address for the target CPU. |
| 108 bool IsValidAddress(jlong address) { | 83 bool IsValidAddress(jlong address) { |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 133 *field_id = env->GetFieldID(clazz, field_name, field_sig); | 108 *field_id = env->GetFieldID(clazz, field_name, field_sig); |
| 134 if (!*field_id) { | 109 if (!*field_id) { |
| 135 LOG_ERROR("Could not find ID for field '%s'", field_name); | 110 LOG_ERROR("Could not find ID for field '%s'", field_name); |
| 136 return false; | 111 return false; |
| 137 } | 112 } |
| 138 LOG_INFO( | 113 LOG_INFO( |
| 139 "%s: Found ID %p for field '%s'", __FUNCTION__, *field_id, field_name); | 114 "%s: Found ID %p for field '%s'", __FUNCTION__, *field_id, field_name); |
| 140 return true; | 115 return true; |
| 141 } | 116 } |
| 142 | 117 |
| 118 // Initialize a jmethodID corresponding to the static method of a given | |
| 119 // |clazz|, with name |method_name| and signature |method_sig|. | |
| 120 // |env| is the current JNI environment handle. | |
| 121 // On success, return true and set |*method_id|. | |
| 122 bool InitStaticMethodId(JNIEnv* env, | |
| 123 jclass clazz, | |
| 124 const char* method_name, | |
| 125 const char* method_sig, | |
| 126 jmethodID* method_id) { | |
| 127 *method_id = env->GetStaticMethodID(clazz, method_name, method_sig); | |
| 128 if (!*method_id) { | |
| 129 LOG_ERROR("Could not find ID for static method '%s'", method_name); | |
| 130 return false; | |
| 131 } | |
| 132 LOG_INFO("%s: Found ID %p for static method '%s'", | |
| 133 __FUNCTION__, *method_id, method_name); | |
| 134 return true; | |
| 135 } | |
| 136 | |
| 143 // A class used to model the field IDs of the org.chromium.base.Linker | 137 // A class used to model the field IDs of the org.chromium.base.Linker |
| 144 // LibInfo inner class, used to communicate data with the Java side | 138 // LibInfo inner class, used to communicate data with the Java side |
| 145 // of the linker. | 139 // of the linker. |
| 146 struct LibInfo_class { | 140 struct LibInfo_class { |
| 147 jfieldID load_address_id; | 141 jfieldID load_address_id; |
| 148 jfieldID load_size_id; | 142 jfieldID load_size_id; |
| 149 jfieldID relro_start_id; | 143 jfieldID relro_start_id; |
| 150 jfieldID relro_size_id; | 144 jfieldID relro_size_id; |
| 151 jfieldID relro_fd_id; | 145 jfieldID relro_fd_id; |
| 152 | 146 |
| (...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 214 | 208 |
| 215 // Ensure libraries located in the same directory as the linker | 209 // Ensure libraries located in the same directory as the linker |
| 216 // can be loaded before system ones. | 210 // can be loaded before system ones. |
| 217 crazy_context_add_search_path_for_address( | 211 crazy_context_add_search_path_for_address( |
| 218 s_crazy_context, reinterpret_cast<void*>(&s_crazy_context)); | 212 s_crazy_context, reinterpret_cast<void*>(&s_crazy_context)); |
| 219 } | 213 } |
| 220 | 214 |
| 221 return s_crazy_context; | 215 return s_crazy_context; |
| 222 } | 216 } |
| 223 | 217 |
| 218 // A scoped crazy_library_t that automatically closes the handle | |
| 219 // on scope exit, unless Release() has been called. | |
| 220 class ScopedLibrary { | |
| 221 public: | |
| 222 ScopedLibrary() : lib_(NULL) {} | |
| 223 | |
| 224 ~ScopedLibrary() { | |
| 225 if (lib_) | |
| 226 crazy_library_close_with_context(lib_, GetCrazyContext()); | |
| 227 } | |
| 228 | |
| 229 crazy_library_t* Get() { return lib_; } | |
| 230 | |
| 231 crazy_library_t** GetPtr() { return &lib_; } | |
| 232 | |
| 233 crazy_library_t* Release() { | |
| 234 crazy_library_t* ret = lib_; | |
| 235 lib_ = NULL; | |
| 236 return ret; | |
| 237 } | |
| 238 | |
| 239 private: | |
| 240 crazy_library_t* lib_; | |
| 241 }; | |
| 242 | |
| 224 // Load a library with the chromium linker. This will also call its | 243 // Load a library with the chromium linker. This will also call its |
| 225 // JNI_OnLoad() method, which shall register its methods. Note that | 244 // JNI_OnLoad() method, which shall register its methods. Note that |
| 226 // lazy native method resolution will _not_ work after this, because | 245 // lazy native method resolution will _not_ work after this, because |
| 227 // Dalvik uses the system's dlsym() which won't see the new library, | 246 // Dalvik uses the system's dlsym() which won't see the new library, |
| 228 // so explicit registration is mandatory. | 247 // so explicit registration is mandatory. |
| 229 // |env| is the current JNI environment handle. | 248 // |env| is the current JNI environment handle. |
| 230 // |clazz| is the static class handle for org.chromium.base.Linker, | 249 // |clazz| is the static class handle for org.chromium.base.Linker, |
| 231 // and is ignored here. | 250 // and is ignored here. |
| 232 // |library_name| is the library name (e.g. libfoo.so). | 251 // |library_name| is the library name (e.g. libfoo.so). |
| 233 // |load_address| is an explicit load address. | 252 // |load_address| is an explicit load address. |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 275 | 294 |
| 276 // Release library object to keep it alive after the function returns. | 295 // Release library object to keep it alive after the function returns. |
| 277 library.Release(); | 296 library.Release(); |
| 278 | 297 |
| 279 s_lib_info_fields.SetLoadInfo( | 298 s_lib_info_fields.SetLoadInfo( |
| 280 env, lib_info_obj, info.load_address, info.load_size); | 299 env, lib_info_obj, info.load_address, info.load_size); |
| 281 LOG_INFO("%s: Success loading library %s", __FUNCTION__, lib_basename); | 300 LOG_INFO("%s: Success loading library %s", __FUNCTION__, lib_basename); |
| 282 return true; | 301 return true; |
| 283 } | 302 } |
| 284 | 303 |
| 304 // Class holding the Java class and method ID for the Java side Linker | |
| 305 // postCallbackOnMainThread method. | |
| 306 struct CallbackRefs_class { | |
|
bulach
2014/05/07 16:10:53
nit: the name should be CallbackRefsClass. however
simonb1
2014/05/07 17:25:24
Done.
| |
| 307 jclass clazz; | |
| 308 jmethodID method_id; | |
| 309 | |
| 310 // Initialize an instance. | |
| 311 bool Init(JNIEnv* env, jclass linker_class) { | |
| 312 clazz = linker_class; | |
|
bulach
2014/05/07 16:10:53
nit: theoretically linker_class is a "localref" th
simonb1
2014/05/07 17:25:24
Done.
| |
| 313 return InitStaticMethodId(env, | |
| 314 linker_class, | |
| 315 "postCallbackOnMainThread", | |
| 316 "(J)V", | |
| 317 &method_id); | |
| 318 } | |
| 319 }; | |
| 320 | |
| 321 static CallbackRefs_class s_callback_refs; | |
| 322 | |
| 323 // Designated receiver function for callbacks from Java. Its name is known | |
| 324 // to the Java side. | |
| 325 // |env| is the current JNI environment handle and is ignored here. | |
| 326 // |clazz| is the static class handle for org.chromium.base.Linker, | |
| 327 // and is ignored here. | |
| 328 // |arg| is a pointer to an allocated crazy_callback_t, deleted after use. | |
| 329 void RunCallbackOnUiThread(JNIEnv* env, jclass clazz, jlong arg) { | |
| 330 crazy_callback_t* callback = reinterpret_cast<crazy_callback_t*>(arg); | |
| 331 | |
| 332 LOG_INFO("%s: Called back from java with handler %p, opaque %p", | |
| 333 __FUNCTION__, callback->handler, callback->opaque); | |
| 334 | |
| 335 crazy_callback_run(callback); | |
| 336 delete callback; | |
| 337 } | |
| 338 | |
| 339 // Request a callback from Java. The supplied crazy_callback_t is valid only | |
| 340 // for the duration of this call, so we copy it to a newly allocated | |
| 341 // crazy_callback_t and then call the Java side's postCallbackOnMainThread. | |
| 342 // This will call back to to our RunCallbackOnUiThread some time | |
| 343 // later on the UI thread. | |
| 344 // |callback_request| is a crazy_callback_t. | |
| 345 // |poster_opaque| is unused. | |
| 346 // Returns true if the callback request succeeds. | |
| 347 static bool PostForLaterExecution(crazy_callback_t* callback_request, | |
| 348 void* poster_opaque UNUSED) { | |
| 349 crazy_context_t* context = GetCrazyContext(); | |
| 350 | |
| 351 JavaVM* vm; | |
| 352 int minimum_jni_version; | |
| 353 crazy_context_get_java_vm(context, | |
| 354 reinterpret_cast<void**>(&vm), | |
| 355 &minimum_jni_version); | |
| 356 | |
| 357 // Do not reuse JNIEnv from JNI_OnLoad, but retrieve our own. | |
| 358 JNIEnv* env; | |
| 359 if (JNI_OK != vm->GetEnv( | |
| 360 reinterpret_cast<void**>(&env), minimum_jni_version)) { | |
| 361 LOG_ERROR("Could not create JNIEnv"); | |
| 362 return false; | |
| 363 } | |
| 364 | |
| 365 // Copy the callback; the one passed as an argument may be temporary. | |
| 366 crazy_callback_t* callback = new crazy_callback_t(); | |
| 367 *callback = *callback_request; | |
| 368 | |
| 369 LOG_INFO("%s: Calling back to java with handler %p, opaque %p", | |
| 370 __FUNCTION__, callback->handler, callback->opaque); | |
| 371 | |
| 372 jlong arg = static_cast<jlong>(reinterpret_cast<intptr_t>(callback)); | |
| 373 env->CallStaticVoidMethod( | |
| 374 s_callback_refs.clazz, s_callback_refs.method_id, arg); | |
| 375 | |
| 376 // Back out and return false if we encounter a JNI exception. | |
| 377 if (env->ExceptionCheck() == JNI_TRUE) { | |
| 378 env->ExceptionDescribe(); | |
| 379 env->ExceptionClear(); | |
| 380 delete callback; | |
| 381 return false; | |
| 382 } | |
| 383 | |
| 384 return true; | |
| 385 } | |
| 386 | |
| 285 jboolean CreateSharedRelro(JNIEnv* env, | 387 jboolean CreateSharedRelro(JNIEnv* env, |
| 286 jclass clazz, | 388 jclass clazz, |
| 287 jstring library_name, | 389 jstring library_name, |
| 288 jlong load_address, | 390 jlong load_address, |
| 289 jobject lib_info_obj) { | 391 jobject lib_info_obj) { |
| 290 String lib_name(env, library_name); | 392 String lib_name(env, library_name); |
| 291 | 393 |
| 292 LOG_INFO("%s: Called for %s", __FUNCTION__, lib_name.c_str()); | 394 LOG_INFO("%s: Called for %s", __FUNCTION__, lib_name.c_str()); |
| 293 | 395 |
| 294 if (!IsValidAddress(load_address)) { | 396 if (!IsValidAddress(load_address)) { |
| (...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 384 | 486 |
| 385 const JNINativeMethod kNativeMethods[] = { | 487 const JNINativeMethod kNativeMethods[] = { |
| 386 {"nativeLoadLibrary", | 488 {"nativeLoadLibrary", |
| 387 "(" | 489 "(" |
| 388 "Ljava/lang/String;" | 490 "Ljava/lang/String;" |
| 389 "J" | 491 "J" |
| 390 "Lorg/chromium/base/library_loader/Linker$LibInfo;" | 492 "Lorg/chromium/base/library_loader/Linker$LibInfo;" |
| 391 ")" | 493 ")" |
| 392 "Z", | 494 "Z", |
| 393 reinterpret_cast<void*>(&LoadLibrary)}, | 495 reinterpret_cast<void*>(&LoadLibrary)}, |
| 496 {"nativeRunCallbackOnUiThread", | |
| 497 "(" | |
| 498 "J" | |
| 499 ")" | |
| 500 "V", | |
| 501 reinterpret_cast<void*>(&RunCallbackOnUiThread)}, | |
| 394 {"nativeCreateSharedRelro", | 502 {"nativeCreateSharedRelro", |
| 395 "(" | 503 "(" |
| 396 "Ljava/lang/String;" | 504 "Ljava/lang/String;" |
| 397 "J" | 505 "J" |
| 398 "Lorg/chromium/base/library_loader/Linker$LibInfo;" | 506 "Lorg/chromium/base/library_loader/Linker$LibInfo;" |
| 399 ")" | 507 ")" |
| 400 "Z", | 508 "Z", |
| 401 reinterpret_cast<void*>(&CreateSharedRelro)}, | 509 reinterpret_cast<void*>(&CreateSharedRelro)}, |
| 402 {"nativeUseSharedRelro", | 510 {"nativeUseSharedRelro", |
| 403 "(" | 511 "(" |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 426 LOG_INFO("%s: Entering", __FUNCTION__); | 534 LOG_INFO("%s: Entering", __FUNCTION__); |
| 427 // Get new JNIEnv | 535 // Get new JNIEnv |
| 428 JNIEnv* env; | 536 JNIEnv* env; |
| 429 if (JNI_OK != vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4)) { | 537 if (JNI_OK != vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4)) { |
| 430 LOG_ERROR("Could not create JNIEnv"); | 538 LOG_ERROR("Could not create JNIEnv"); |
| 431 return -1; | 539 return -1; |
| 432 } | 540 } |
| 433 | 541 |
| 434 // Register native methods. | 542 // Register native methods. |
| 435 jclass linker_class; | 543 jclass linker_class; |
| 436 if (!InitClassReference( | 544 if (!InitClassReference(env, |
| 437 env, "org/chromium/base/library_loader/Linker", &linker_class)) | 545 "org/chromium/base/library_loader/Linker", |
| 546 &linker_class)) | |
| 438 return -1; | 547 return -1; |
| 439 | 548 |
| 440 LOG_INFO("%s: Registering native methods", __FUNCTION__); | 549 LOG_INFO("%s: Registering native methods", __FUNCTION__); |
| 441 env->RegisterNatives(linker_class, | 550 env->RegisterNatives(linker_class, |
| 442 kNativeMethods, | 551 kNativeMethods, |
| 443 sizeof(kNativeMethods) / sizeof(kNativeMethods[0])); | 552 sizeof(kNativeMethods) / sizeof(kNativeMethods[0])); |
| 444 | 553 |
| 445 // Find LibInfo field ids. | 554 // Find LibInfo field ids. |
| 446 LOG_INFO("%s: Caching field IDs", __FUNCTION__); | 555 LOG_INFO("%s: Caching field IDs", __FUNCTION__); |
| 447 if (!s_lib_info_fields.Init(env)) { | 556 if (!s_lib_info_fields.Init(env)) { |
| 448 return -1; | 557 return -1; |
| 449 } | 558 } |
| 450 | 559 |
| 560 // Resolve and save the Java side Linker callback class and method. | |
| 561 LOG_INFO("%s: Resolving callback references", __FUNCTION__); | |
| 562 if (!s_callback_refs.Init(env, linker_class)) { | |
| 563 return -1; | |
| 564 } | |
| 565 | |
| 451 // Save JavaVM* handle into context. | 566 // Save JavaVM* handle into context. |
| 452 crazy_context_t* context = GetCrazyContext(); | 567 crazy_context_t* context = GetCrazyContext(); |
| 453 crazy_context_set_java_vm(context, vm, JNI_VERSION_1_4); | 568 crazy_context_set_java_vm(context, vm, JNI_VERSION_1_4); |
| 454 | 569 |
| 570 // Register the function that the crazy linker can call to post code | |
| 571 // for later execution. | |
| 572 crazy_context_set_callback_poster(context, &PostForLaterExecution, NULL); | |
| 573 | |
| 455 LOG_INFO("%s: Done", __FUNCTION__); | 574 LOG_INFO("%s: Done", __FUNCTION__); |
| 456 return JNI_VERSION_1_4; | 575 return JNI_VERSION_1_4; |
| 457 } | 576 } |
| OLD | NEW |