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>
20 #include "linker_jni.h"
22 namespace chromium_android_linker
{
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
28 // |env| is the current JNI environment handle.
29 // On success, return true.
30 bool InitSDKVersionInfo(JNIEnv
* env
) {
32 if (!InitStaticInt(env
, "android/os/Build$VERSION", "SDK_INT", &value
))
35 crazy_set_sdk_build_version(static_cast<int>(value
));
36 LOG_INFO("Set SDK build version to %d", static_cast<int>(value
));
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.
64 ScopedLibrary() : lib_(nullptr) {}
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_
;
82 crazy_library_t
* lib_
;
85 template <class LibraryOpener
>
86 bool GenericLoadLibrary(JNIEnv
* env
,
87 const char* library_name
,
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
);
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
)) {
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
));
114 // Release library object to keep it alive after the function returns.
117 s_lib_info_fields
.SetLoadInfo(env
,
119 info
.load_address
, info
.load_size
);
120 LOG_INFO("Success loading library %s", library_name
);
124 // Used for opening the library in a regular file.
125 class FileLibraryOpener
{
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
));
143 // Used for opening the library in a zip file.
144 class ZipLibraryOpener
{
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;
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
,
161 LOG_ERROR("Could not open %s in zip file %s: %s",
162 library_name
, zip_file_
, crazy_context_get_error(context
));
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
,
184 jstring library_name
,
186 jobject lib_info_obj
) {
187 String
lib_name(env
, library_name
);
188 FileLibraryOpener opener
;
190 return GenericLoadLibrary(env
,
192 static_cast<size_t>(load_address
),
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
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
,
221 jstring zipfile_name
,
222 jstring library_name
,
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
,
231 static_cast<size_t>(load_address
),
236 // Class holding the Java class and method ID for the Java side Linker
237 // postCallbackOnMainThread method.
238 struct JavaCallbackBindings_class
{
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
,
247 "postCallbackOnMainThread",
253 static JavaCallbackBindings_class s_java_callback_bindings
;
255 // Designated receiver function for callbacks from Java. Its name is known
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
);
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();
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.
291 if (JNI_OK
!= vm
->GetEnv(
292 reinterpret_cast<void**>(&env
), minimum_jni_version
)) {
293 LOG_ERROR("Could not create JNIEnv");
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();
320 jboolean
CreateSharedRelro(JNIEnv
* env
,
322 jstring library_name
,
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
);
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());
340 crazy_context_t
* context
= GetCrazyContext();
341 size_t relro_start
= 0;
342 size_t relro_size
= 0;
345 if (!crazy_library_create_shared_relro(library
.Get(),
347 static_cast<size_t>(load_address
),
351 LOG_ERROR("Could not create shared RELRO sharing for %s: %s\n",
352 lib_name
.c_str(), crazy_context_get_error(context
));
356 s_lib_info_fields
.SetRelroInfo(env
,
358 relro_start
, relro_size
, relro_fd
);
362 jboolean
UseSharedRelro(JNIEnv
* env
,
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());
376 crazy_context_t
* context
= GetCrazyContext();
377 size_t relro_start
= 0;
378 size_t relro_size
= 0;
380 s_lib_info_fields
.GetRelroInfo(env
,
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(),
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
));
395 LOG_INFO("Library %s using shared RELRO section!", lib_name
.c_str());
400 const JNINativeMethod kNativeMethods
[] = {
401 {"nativeLoadLibrary",
405 "Lorg/chromium/base/library_loader/Linker$LibInfo;"
408 reinterpret_cast<void*>(&LoadLibrary
)},
409 {"nativeLoadLibraryInZipFile",
414 "Lorg/chromium/base/library_loader/Linker$LibInfo;"
417 reinterpret_cast<void*>(&LoadLibraryInZipFile
)},
418 {"nativeRunCallbackOnUiThread",
423 reinterpret_cast<void*>(&RunCallbackOnUiThread
)},
424 {"nativeCreateSharedRelro",
428 "Lorg/chromium/base/library_loader/Linker$LibInfo;"
431 reinterpret_cast<void*>(&CreateSharedRelro
)},
432 {"nativeUseSharedRelro",
435 "Lorg/chromium/base/library_loader/Linker$LibInfo;"
438 reinterpret_cast<void*>(&UseSharedRelro
)},
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
))
451 // Register native methods.
453 if (!InitClassReference(env
,
454 "org/chromium/base/library_loader/LegacyLinker",
458 LOG_INFO("Registering native methods");
459 env
->RegisterNatives(linker_class
,
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
)) {
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);
480 } // namespace chromium_android_linker