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