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 version of the Android-specific Chromium linker that uses 6 // the crazy linker to load libraries. 7 8 // This source code *cannot* depend on anything from base/ or the C++ 9 // STL, to keep the final library small, and avoid ugly dependency issues. 10 11 #include "legacy_linker_jni.h" 12 13 #include <crazy_linker.h> 14 #include <fcntl.h> 15 #include <jni.h> 16 #include <limits.h> 17 #include <stddef.h> 18 #include <stdlib.h> 19 #include <unistd.h> 20 21 #include "linker_jni.h" 22 23 namespace chromium_android_linker { 24 namespace { 25 26 // Retrieve the SDK build version and pass it into the crazy linker. This 27 // needs to be done early in initialization, before any other crazy linker 28 // code is run. 29 // |env| is the current JNI environment handle. 30 // On success, return true. 31 bool InitSDKVersionInfo(JNIEnv* env) { 32 jint value = 0; 33 if (!InitStaticInt(env, "android/os/Build$VERSION", "SDK_INT", &value)) 34 return false; 35 36 crazy_set_sdk_build_version(static_cast<int>(value)); 37 LOG_INFO("Set SDK build version to %d", static_cast<int>(value)); 38 39 return true; 40 } 41 42 // The linker uses a single crazy_context_t object created on demand. 43 // There is no need to protect this against concurrent access, locking 44 // is already handled on the Java side. 45 crazy_context_t* GetCrazyContext() { 46 static crazy_context_t* s_crazy_context = nullptr; 47 48 if (!s_crazy_context) { 49 // Create new context. 50 s_crazy_context = crazy_context_create(); 51 52 // Ensure libraries located in the same directory as the linker 53 // can be loaded before system ones. 54 crazy_context_add_search_path_for_address( 55 s_crazy_context, reinterpret_cast<void*>(&s_crazy_context)); 56 } 57 58 return s_crazy_context; 59 } 60 61 // A scoped crazy_library_t that automatically closes the handle 62 // on scope exit, unless Release() has been called. 63 class ScopedLibrary { 64 public: 65 ScopedLibrary() : lib_(nullptr) {} 66 67 ~ScopedLibrary() { 68 if (lib_) 69 crazy_library_close_with_context(lib_, GetCrazyContext()); 70 } 71 72 crazy_library_t* Get() { return lib_; } 73 74 crazy_library_t** GetPtr() { return &lib_; } 75 76 crazy_library_t* Release() { 77 crazy_library_t* ret = lib_; 78 lib_ = nullptr; 79 return ret; 80 } 81 82 private: 83 crazy_library_t* lib_; 84 }; 85 86 template <class LibraryOpener> 87 bool GenericLoadLibrary(JNIEnv* env, 88 const char* library_name, 89 jlong load_address, 90 jobject lib_info_obj, 91 const LibraryOpener& opener) { 92 LOG_INFO("Called for %s, at address 0x%llx", library_name, load_address); 93 crazy_context_t* context = GetCrazyContext(); 94 95 if (!IsValidAddress(load_address)) { 96 LOG_ERROR("Invalid address 0x%llx", load_address); 97 return false; 98 } 99 100 // Set the desired load address (0 means randomize it). 101 crazy_context_set_load_address(context, static_cast<size_t>(load_address)); 102 103 ScopedLibrary library; 104 if (!opener.Open(library.GetPtr(), library_name, context)) { 105 return false; 106 } 107 108 crazy_library_info_t info; 109 if (!crazy_library_get_info(library.Get(), context, &info)) { 110 LOG_ERROR("Could not get library information for %s: %s", 111 library_name, crazy_context_get_error(context)); 112 return false; 113 } 114 115 // Release library object to keep it alive after the function returns. 116 library.Release(); 117 118 s_lib_info_fields.SetLoadInfo(env, 119 lib_info_obj, 120 info.load_address, info.load_size); 121 LOG_INFO("Success loading library %s", library_name); 122 return true; 123 } 124 125 // Used for opening the library in a regular file. 126 class FileLibraryOpener { 127 public: 128 bool Open(crazy_library_t** library, 129 const char* library_name, 130 crazy_context_t* context) const; 131 }; 132 133 bool FileLibraryOpener::Open(crazy_library_t** library, 134 const char* library_name, 135 crazy_context_t* context) const { 136 if (!crazy_library_open(library, library_name, context)) { 137 LOG_ERROR("Could not open %s: %s", 138 library_name, crazy_context_get_error(context)); 139 return false; 140 } 141 return true; 142 } 143 144 // Used for opening the library in a zip file. 145 class ZipLibraryOpener { 146 public: 147 explicit ZipLibraryOpener(const char* zip_file) : zip_file_(zip_file) { } 148 bool Open(crazy_library_t** library, 149 const char* library_name, 150 crazy_context_t* context) const; 151 private: 152 const char* zip_file_; 153 }; 154 155 bool ZipLibraryOpener::Open(crazy_library_t** library, 156 const char* library_name, 157 crazy_context_t* context) const { 158 if (!crazy_library_open_in_zip_file(library, 159 zip_file_, 160 library_name, 161 context)) { 162 LOG_ERROR("Could not open %s in zip file %s: %s", 163 library_name, zip_file_, crazy_context_get_error(context)); 164 return false; 165 } 166 return true; 167 } 168 169 // Load a library with the chromium linker. This will also call its 170 // JNI_OnLoad() method, which shall register its methods. Note that 171 // lazy native method resolution will _not_ work after this, because 172 // Dalvik uses the system's dlsym() which won't see the new library, 173 // so explicit registration is mandatory. 174 // 175 // |env| is the current JNI environment handle. 176 // |clazz| is the static class handle for org.chromium.base.Linker, 177 // and is ignored here. 178 // |library_name| is the library name (e.g. libfoo.so). 179 // |load_address| is an explicit load address. 180 // |library_info| is a LibInfo handle used to communicate information 181 // with the Java side. 182 // Return true on success. 183 jboolean LoadLibrary(JNIEnv* env, 184 jclass clazz, 185 jstring library_name, 186 jlong load_address, 187 jobject lib_info_obj) { 188 String lib_name(env, library_name); 189 FileLibraryOpener opener; 190 191 return GenericLoadLibrary(env, 192 lib_name.c_str(), 193 static_cast<size_t>(load_address), 194 lib_info_obj, 195 opener); 196 } 197 198 // Load a library from a zipfile with the chromium linker. The 199 // library in the zipfile must be uncompressed and page aligned. 200 // The basename of the library is given. The library is expected 201 // to be lib/<abi_tag>/crazy.<basename>. The <abi_tag> used will be the 202 // same as the abi for this linker. The "crazy." prefix is included 203 // so that the Android Package Manager doesn't extract the library into 204 // /data/app-lib. 205 // 206 // Loading the library will also call its JNI_OnLoad() method, which 207 // shall register its methods. Note that lazy native method resolution 208 // will _not_ work after this, because Dalvik uses the system's dlsym() 209 // which won't see the new library, so explicit registration is mandatory. 210 // 211 // |env| is the current JNI environment handle. 212 // |clazz| is the static class handle for org.chromium.base.Linker, 213 // and is ignored here. 214 // |zipfile_name| is the filename of the zipfile containing the library. 215 // |library_name| is the library base name (e.g. libfoo.so). 216 // |load_address| is an explicit load address. 217 // |library_info| is a LibInfo handle used to communicate information 218 // with the Java side. 219 // Returns true on success. 220 jboolean LoadLibraryInZipFile(JNIEnv* env, 221 jclass clazz, 222 jstring zipfile_name, 223 jstring library_name, 224 jlong load_address, 225 jobject lib_info_obj) { 226 String zipfile_name_str(env, zipfile_name); 227 String lib_name(env, library_name); 228 ZipLibraryOpener opener(zipfile_name_str.c_str()); 229 230 return GenericLoadLibrary(env, 231 lib_name.c_str(), 232 static_cast<size_t>(load_address), 233 lib_info_obj, 234 opener); 235 } 236 237 // Class holding the Java class and method ID for the Java side Linker 238 // postCallbackOnMainThread method. 239 struct JavaCallbackBindings_class { 240 jclass clazz; 241 jmethodID method_id; 242 243 // Initialize an instance. 244 bool Init(JNIEnv* env, jclass linker_class) { 245 clazz = reinterpret_cast<jclass>(env->NewGlobalRef(linker_class)); 246 return InitStaticMethodId(env, 247 linker_class, 248 "postCallbackOnMainThread", 249 "(J)V", 250 &method_id); 251 } 252 }; 253 254 static JavaCallbackBindings_class s_java_callback_bindings; 255 256 // Designated receiver function for callbacks from Java. Its name is known 257 // to the Java side. 258 // |env| is the current JNI environment handle and is ignored here. 259 // |clazz| is the static class handle for org.chromium.base.Linker, 260 // and is ignored here. 261 // |arg| is a pointer to an allocated crazy_callback_t, deleted after use. 262 void RunCallbackOnUiThread(JNIEnv* env, jclass clazz, jlong arg) { 263 crazy_callback_t* callback = reinterpret_cast<crazy_callback_t*>(arg); 264 265 LOG_INFO("Called back from java with handler %p, opaque %p", 266 callback->handler, callback->opaque); 267 268 crazy_callback_run(callback); 269 delete callback; 270 } 271 272 // Request a callback from Java. The supplied crazy_callback_t is valid only 273 // for the duration of this call, so we copy it to a newly allocated 274 // crazy_callback_t and then call the Java side's postCallbackOnMainThread. 275 // This will call back to to our RunCallbackOnUiThread some time 276 // later on the UI thread. 277 // |callback_request| is a crazy_callback_t. 278 // |poster_opaque| is unused. 279 // Returns true if the callback request succeeds. 280 static bool PostForLaterExecution(crazy_callback_t* callback_request, 281 void* poster_opaque UNUSED) { 282 crazy_context_t* context = GetCrazyContext(); 283 284 JavaVM* vm; 285 int minimum_jni_version; 286 crazy_context_get_java_vm(context, 287 reinterpret_cast<void**>(&vm), 288 &minimum_jni_version); 289 290 // Do not reuse JNIEnv from JNI_OnLoad, but retrieve our own. 291 JNIEnv* env; 292 if (JNI_OK != vm->GetEnv( 293 reinterpret_cast<void**>(&env), minimum_jni_version)) { 294 LOG_ERROR("Could not create JNIEnv"); 295 return false; 296 } 297 298 // Copy the callback; the one passed as an argument may be temporary. 299 crazy_callback_t* callback = new crazy_callback_t(); 300 *callback = *callback_request; 301 302 LOG_INFO("Calling back to java with handler %p, opaque %p", 303 callback->handler, callback->opaque); 304 305 jlong arg = static_cast<jlong>(reinterpret_cast<uintptr_t>(callback)); 306 307 env->CallStaticVoidMethod( 308 s_java_callback_bindings.clazz, s_java_callback_bindings.method_id, arg); 309 310 // Back out and return false if we encounter a JNI exception. 311 if (env->ExceptionCheck() == JNI_TRUE) { 312 env->ExceptionDescribe(); 313 env->ExceptionClear(); 314 delete callback; 315 return false; 316 } 317 318 return true; 319 } 320 321 jboolean CreateSharedRelro(JNIEnv* env, 322 jclass clazz, 323 jstring library_name, 324 jlong load_address, 325 jobject lib_info_obj) { 326 String lib_name(env, library_name); 327 328 LOG_INFO("Called for %s", lib_name.c_str()); 329 330 if (!IsValidAddress(load_address)) { 331 LOG_ERROR("Invalid address 0x%llx", load_address); 332 return false; 333 } 334 335 ScopedLibrary library; 336 if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) { 337 LOG_ERROR("Could not find %s", lib_name.c_str()); 338 return false; 339 } 340 341 crazy_context_t* context = GetCrazyContext(); 342 size_t relro_start = 0; 343 size_t relro_size = 0; 344 int relro_fd = -1; 345 346 if (!crazy_library_create_shared_relro(library.Get(), 347 context, 348 static_cast<size_t>(load_address), 349 &relro_start, 350 &relro_size, 351 &relro_fd)) { 352 LOG_ERROR("Could not create shared RELRO sharing for %s: %s\n", 353 lib_name.c_str(), crazy_context_get_error(context)); 354 return false; 355 } 356 357 s_lib_info_fields.SetRelroInfo(env, 358 lib_info_obj, 359 relro_start, relro_size, relro_fd); 360 return true; 361 } 362 363 jboolean UseSharedRelro(JNIEnv* env, 364 jclass clazz, 365 jstring library_name, 366 jobject lib_info_obj) { 367 String lib_name(env, library_name); 368 369 LOG_INFO("Called for %s, lib_info_ref=%p", lib_name.c_str(), lib_info_obj); 370 371 ScopedLibrary library; 372 if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) { 373 LOG_ERROR("Could not find %s", lib_name.c_str()); 374 return false; 375 } 376 377 crazy_context_t* context = GetCrazyContext(); 378 size_t relro_start = 0; 379 size_t relro_size = 0; 380 int relro_fd = -1; 381 s_lib_info_fields.GetRelroInfo(env, 382 lib_info_obj, 383 &relro_start, &relro_size, &relro_fd); 384 385 LOG_INFO("library=%s relro start=%p size=%p fd=%d", 386 lib_name.c_str(), (void*)relro_start, (void*)relro_size, relro_fd); 387 388 if (!crazy_library_use_shared_relro(library.Get(), 389 context, 390 relro_start, relro_size, relro_fd)) { 391 LOG_ERROR("Could not use shared RELRO for %s: %s", 392 lib_name.c_str(), crazy_context_get_error(context)); 393 return false; 394 } 395 396 LOG_INFO("Library %s using shared RELRO section!", lib_name.c_str()); 397 398 return true; 399 } 400 401 const JNINativeMethod kNativeMethods[] = { 402 {"nativeLoadLibrary", 403 "(" 404 "Ljava/lang/String;" 405 "J" 406 "Lorg/chromium/base/library_loader/Linker$LibInfo;" 407 ")" 408 "Z", 409 reinterpret_cast<void*>(&LoadLibrary)}, 410 {"nativeLoadLibraryInZipFile", 411 "(" 412 "Ljava/lang/String;" 413 "Ljava/lang/String;" 414 "J" 415 "Lorg/chromium/base/library_loader/Linker$LibInfo;" 416 ")" 417 "Z", 418 reinterpret_cast<void*>(&LoadLibraryInZipFile)}, 419 {"nativeRunCallbackOnUiThread", 420 "(" 421 "J" 422 ")" 423 "V", 424 reinterpret_cast<void*>(&RunCallbackOnUiThread)}, 425 {"nativeCreateSharedRelro", 426 "(" 427 "Ljava/lang/String;" 428 "J" 429 "Lorg/chromium/base/library_loader/Linker$LibInfo;" 430 ")" 431 "Z", 432 reinterpret_cast<void*>(&CreateSharedRelro)}, 433 {"nativeUseSharedRelro", 434 "(" 435 "Ljava/lang/String;" 436 "Lorg/chromium/base/library_loader/Linker$LibInfo;" 437 ")" 438 "Z", 439 reinterpret_cast<void*>(&UseSharedRelro)}, 440 }; 441 442 const size_t kNumNativeMethods = 443 sizeof(kNativeMethods) / sizeof(kNativeMethods[0]); 444 445 } // namespace 446 447 bool LegacyLinkerJNIInit(JavaVM* vm, JNIEnv* env) { 448 LOG_INFO("Entering"); 449 450 // Initialize SDK version info. 451 LOG_INFO("Retrieving SDK version info"); 452 if (!InitSDKVersionInfo(env)) 453 return false; 454 455 // Register native methods. 456 jclass linker_class; 457 if (!InitClassReference(env, 458 "org/chromium/base/library_loader/LegacyLinker", 459 &linker_class)) 460 return false; 461 462 LOG_INFO("Registering native methods"); 463 if (env->RegisterNatives(linker_class, kNativeMethods, kNumNativeMethods) < 0) 464 return false; 465 466 // Resolve and save the Java side Linker callback class and method. 467 LOG_INFO("Resolving callback bindings"); 468 if (!s_java_callback_bindings.Init(env, linker_class)) { 469 return false; 470 } 471 472 // Save JavaVM* handle into context. 473 crazy_context_t* context = GetCrazyContext(); 474 crazy_context_set_java_vm(context, vm, JNI_VERSION_1_4); 475 476 // Register the function that the crazy linker can call to post code 477 // for later execution. 478 crazy_context_set_callback_poster(context, &PostForLaterExecution, nullptr); 479 480 return true; 481 } 482 483 } // namespace chromium_android_linker 484