b0b464b0d4eb8f2d1fb6292eccf89387959ac241
[platform/framework/web/crosswalk.git] / src / base / android / linker / linker_jni.cc
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 true iff |address| is a valid address for the target CPU.
74 bool IsValidAddress(jlong address) {
75   return static_cast<jlong>(static_cast<size_t>(address)) == address;
76 }
77
78 // Find the jclass JNI reference corresponding to a given |class_name|.
79 // |env| is the current JNI environment handle.
80 // On success, return true and set |*clazz|.
81 bool InitClassReference(JNIEnv* env, const char* class_name, jclass* clazz) {
82   *clazz = env->FindClass(class_name);
83   if (!*clazz) {
84     LOG_ERROR("Could not find class for %s", class_name);
85     return false;
86   }
87   return true;
88 }
89
90 // Initialize a jfieldID corresponding to the field of a given |clazz|,
91 // with name |field_name| and signature |field_sig|.
92 // |env| is the current JNI environment handle.
93 // On success, return true and set |*field_id|.
94 bool InitFieldId(JNIEnv* env,
95                  jclass clazz,
96                  const char* field_name,
97                  const char* field_sig,
98                  jfieldID* field_id) {
99   *field_id = env->GetFieldID(clazz, field_name, field_sig);
100   if (!*field_id) {
101     LOG_ERROR("Could not find ID for field '%s'", field_name);
102     return false;
103   }
104   LOG_INFO(
105       "%s: Found ID %p for field '%s'", __FUNCTION__, *field_id, field_name);
106   return true;
107 }
108
109 // Initialize a jmethodID corresponding to the static method of a given
110 // |clazz|, with name |method_name| and signature |method_sig|.
111 // |env| is the current JNI environment handle.
112 // On success, return true and set |*method_id|.
113 bool InitStaticMethodId(JNIEnv* env,
114                         jclass clazz,
115                         const char* method_name,
116                         const char* method_sig,
117                         jmethodID* method_id) {
118   *method_id = env->GetStaticMethodID(clazz, method_name, method_sig);
119   if (!*method_id) {
120     LOG_ERROR("Could not find ID for static method '%s'", method_name);
121     return false;
122   }
123   LOG_INFO("%s: Found ID %p for static method '%s'",
124            __FUNCTION__, *method_id, method_name);
125   return true;
126 }
127
128 // A class used to model the field IDs of the org.chromium.base.Linker
129 // LibInfo inner class, used to communicate data with the Java side
130 // of the linker.
131 struct LibInfo_class {
132   jfieldID load_address_id;
133   jfieldID load_size_id;
134   jfieldID relro_start_id;
135   jfieldID relro_size_id;
136   jfieldID relro_fd_id;
137
138   // Initialize an instance.
139   bool Init(JNIEnv* env) {
140     jclass clazz;
141     if (!InitClassReference(
142              env, "org/chromium/base/library_loader/Linker$LibInfo", &clazz)) {
143       return false;
144     }
145
146     return InitFieldId(env, clazz, "mLoadAddress", "J", &load_address_id) &&
147            InitFieldId(env, clazz, "mLoadSize", "J", &load_size_id) &&
148            InitFieldId(env, clazz, "mRelroStart", "J", &relro_start_id) &&
149            InitFieldId(env, clazz, "mRelroSize", "J", &relro_size_id) &&
150            InitFieldId(env, clazz, "mRelroFd", "I", &relro_fd_id);
151   }
152
153   void SetLoadInfo(JNIEnv* env,
154                    jobject library_info_obj,
155                    size_t load_address,
156                    size_t load_size) {
157     env->SetLongField(library_info_obj, load_address_id, load_address);
158     env->SetLongField(library_info_obj, load_size_id, load_size);
159   }
160
161   // Use this instance to convert a RelroInfo reference into
162   // a crazy_library_info_t.
163   void GetRelroInfo(JNIEnv* env,
164                     jobject library_info_obj,
165                     size_t* relro_start,
166                     size_t* relro_size,
167                     int* relro_fd) {
168     *relro_start = static_cast<size_t>(
169         env->GetLongField(library_info_obj, relro_start_id));
170
171     *relro_size =
172         static_cast<size_t>(env->GetLongField(library_info_obj, relro_size_id));
173
174     *relro_fd = env->GetIntField(library_info_obj, relro_fd_id);
175   }
176
177   void SetRelroInfo(JNIEnv* env,
178                     jobject library_info_obj,
179                     size_t relro_start,
180                     size_t relro_size,
181                     int relro_fd) {
182     env->SetLongField(library_info_obj, relro_start_id, relro_start);
183     env->SetLongField(library_info_obj, relro_size_id, relro_size);
184     env->SetIntField(library_info_obj, relro_fd_id, relro_fd);
185   }
186 };
187
188 static LibInfo_class s_lib_info_fields;
189
190 // The linker uses a single crazy_context_t object created on demand.
191 // There is no need to protect this against concurrent access, locking
192 // is already handled on the Java side.
193 static crazy_context_t* s_crazy_context;
194
195 crazy_context_t* GetCrazyContext() {
196   if (!s_crazy_context) {
197     // Create new context.
198     s_crazy_context = crazy_context_create();
199
200     // Ensure libraries located in the same directory as the linker
201     // can be loaded before system ones.
202     crazy_context_add_search_path_for_address(
203         s_crazy_context, reinterpret_cast<void*>(&s_crazy_context));
204   }
205
206   return s_crazy_context;
207 }
208
209 // A scoped crazy_library_t that automatically closes the handle
210 // on scope exit, unless Release() has been called.
211 class ScopedLibrary {
212  public:
213   ScopedLibrary() : lib_(NULL) {}
214
215   ~ScopedLibrary() {
216     if (lib_)
217       crazy_library_close_with_context(lib_, GetCrazyContext());
218   }
219
220   crazy_library_t* Get() { return lib_; }
221
222   crazy_library_t** GetPtr() { return &lib_; }
223
224   crazy_library_t* Release() {
225     crazy_library_t* ret = lib_;
226     lib_ = NULL;
227     return ret;
228   }
229
230  private:
231   crazy_library_t* lib_;
232 };
233
234 namespace {
235
236 template <class LibraryOpener>
237 bool GenericLoadLibrary(
238     JNIEnv* env,
239     const char* library_name, jlong load_address, jobject lib_info_obj,
240     const LibraryOpener& opener) {
241   crazy_context_t* context = GetCrazyContext();
242
243   if (!IsValidAddress(load_address)) {
244     LOG_ERROR("%s: Invalid address 0x%llx", __FUNCTION__, load_address);
245     return false;
246   }
247
248   // Set the desired load address (0 means randomize it).
249   crazy_context_set_load_address(context, static_cast<size_t>(load_address));
250
251   ScopedLibrary library;
252   if (!opener.Open(library.GetPtr(), library_name, context)) {
253     return false;
254   }
255
256   crazy_library_info_t info;
257   if (!crazy_library_get_info(library.Get(), context, &info)) {
258     LOG_ERROR("%s: Could not get library information for %s: %s",
259               __FUNCTION__,
260               library_name,
261               crazy_context_get_error(context));
262     return false;
263   }
264
265   // Release library object to keep it alive after the function returns.
266   library.Release();
267
268   s_lib_info_fields.SetLoadInfo(
269       env, lib_info_obj, info.load_address, info.load_size);
270   LOG_INFO("%s: Success loading library %s", __FUNCTION__, library_name);
271   return true;
272 }
273
274 // Used for opening the library in a regular file.
275 class FileLibraryOpener {
276  public:
277   bool Open(
278       crazy_library_t** library,
279       const char* library_name,
280       crazy_context_t* context) const;
281 };
282
283 bool FileLibraryOpener::Open(
284     crazy_library_t** library,
285     const char* library_name,
286     crazy_context_t* context) const {
287   if (!crazy_library_open(library, library_name, context)) {
288     LOG_ERROR("%s: Could not open %s: %s",
289               __FUNCTION__,
290               library_name,
291               crazy_context_get_error(context));
292     return false;
293   }
294   return true;
295 }
296
297 // Used for opening the library in a zip file.
298 class ZipLibraryOpener {
299  public:
300   explicit ZipLibraryOpener(const char* zip_file) : zip_file_(zip_file) {}
301   bool Open(
302       crazy_library_t** library,
303       const char* library_name,
304       crazy_context_t* context) const;
305  private:
306   const char* zip_file_;
307 };
308
309 bool ZipLibraryOpener::Open(
310     crazy_library_t** library,
311     const char* library_name,
312     crazy_context_t* context) const {
313   if (!crazy_library_open_in_zip_file(
314           library, zip_file_, library_name, context)) {
315      LOG_ERROR("%s: Could not open %s in zip file %s: %s",
316                __FUNCTION__, library_name, zip_file_,
317                crazy_context_get_error(context));
318      return false;
319   }
320   return true;
321 }
322
323 }  // unnamed namespace
324
325 // Load a library with the chromium linker. This will also call its
326 // JNI_OnLoad() method, which shall register its methods. Note that
327 // lazy native method resolution will _not_ work after this, because
328 // Dalvik uses the system's dlsym() which won't see the new library,
329 // so explicit registration is mandatory.
330 // |env| is the current JNI environment handle.
331 // |clazz| is the static class handle for org.chromium.base.Linker,
332 // and is ignored here.
333 // |library_name| is the library name (e.g. libfoo.so).
334 // |load_address| is an explicit load address.
335 // |library_info| is a LibInfo handle used to communicate information
336 // with the Java side.
337 // Return true on success.
338 jboolean LoadLibrary(JNIEnv* env,
339                      jclass clazz,
340                      jstring library_name,
341                      jlong load_address,
342                      jobject lib_info_obj) {
343   String lib_name(env, library_name);
344   FileLibraryOpener opener;
345   return GenericLoadLibrary(
346       env, lib_name.c_str(),
347       static_cast<size_t>(load_address), lib_info_obj, opener);
348 }
349
350 // Load a library from a zipfile with the chromium linker. The
351 // library in the zipfile must be uncompressed and page aligned.
352 // The basename of the library is given. The library is expected
353 // to be lib/<abi_tag>/crazy.<basename>. The <abi_tag> used will be the
354 // same as the abi for this linker. The "crazy." prefix is included
355 // so that the Android Package Manager doesn't extract the library into
356 // /data/app-lib.
357 //
358 // Loading the library will also call its JNI_OnLoad() method, which
359 // shall register its methods. Note that lazy native method resolution
360 // will _not_ work after this, because Dalvik uses the system's dlsym()
361 // which won't see the new library, so explicit registration is mandatory.
362 //
363 // |env| is the current JNI environment handle.
364 // |clazz| is the static class handle for org.chromium.base.Linker,
365 // and is ignored here.
366 // |zipfile_name| is the filename of the zipfile containing the library.
367 // |library_name| is the library base name (e.g. libfoo.so).
368 // |load_address| is an explicit load address.
369 // |library_info| is a LibInfo handle used to communicate information
370 // with the Java side.
371 // Returns true on success.
372 jboolean LoadLibraryInZipFile(JNIEnv* env,
373                               jclass clazz,
374                               jstring zipfile_name,
375                               jstring library_name,
376                               jlong load_address,
377                               jobject lib_info_obj) {
378   String zipfile_name_str(env, zipfile_name);
379   String lib_name(env, library_name);
380   ZipLibraryOpener opener(zipfile_name_str.c_str());
381   return GenericLoadLibrary(
382       env, lib_name.c_str(),
383       static_cast<size_t>(load_address), lib_info_obj, opener);
384 }
385
386 // Class holding the Java class and method ID for the Java side Linker
387 // postCallbackOnMainThread method.
388 struct JavaCallbackBindings_class {
389   jclass clazz;
390   jmethodID method_id;
391
392   // Initialize an instance.
393   bool Init(JNIEnv* env, jclass linker_class) {
394     clazz = reinterpret_cast<jclass>(env->NewGlobalRef(linker_class));
395     return InitStaticMethodId(env,
396                               linker_class,
397                               "postCallbackOnMainThread",
398                               "(J)V",
399                               &method_id);
400   }
401 };
402
403 static JavaCallbackBindings_class s_java_callback_bindings;
404
405 // Designated receiver function for callbacks from Java. Its name is known
406 // to the Java side.
407 // |env| is the current JNI environment handle and is ignored here.
408 // |clazz| is the static class handle for org.chromium.base.Linker,
409 // and is ignored here.
410 // |arg| is a pointer to an allocated crazy_callback_t, deleted after use.
411 void RunCallbackOnUiThread(JNIEnv* env, jclass clazz, jlong arg) {
412   crazy_callback_t* callback = reinterpret_cast<crazy_callback_t*>(arg);
413
414   LOG_INFO("%s: Called back from java with handler %p, opaque %p",
415            __FUNCTION__, callback->handler, callback->opaque);
416
417   crazy_callback_run(callback);
418   delete callback;
419 }
420
421 // Request a callback from Java. The supplied crazy_callback_t is valid only
422 // for the duration of this call, so we copy it to a newly allocated
423 // crazy_callback_t and then call the Java side's postCallbackOnMainThread.
424 // This will call back to to our RunCallbackOnUiThread some time
425 // later on the UI thread.
426 // |callback_request| is a crazy_callback_t.
427 // |poster_opaque| is unused.
428 // Returns true if the callback request succeeds.
429 static bool PostForLaterExecution(crazy_callback_t* callback_request,
430                                   void* poster_opaque UNUSED) {
431   crazy_context_t* context = GetCrazyContext();
432
433   JavaVM* vm;
434   int minimum_jni_version;
435   crazy_context_get_java_vm(context,
436                             reinterpret_cast<void**>(&vm),
437                             &minimum_jni_version);
438
439   // Do not reuse JNIEnv from JNI_OnLoad, but retrieve our own.
440   JNIEnv* env;
441   if (JNI_OK != vm->GetEnv(
442       reinterpret_cast<void**>(&env), minimum_jni_version)) {
443     LOG_ERROR("Could not create JNIEnv");
444     return false;
445   }
446
447   // Copy the callback; the one passed as an argument may be temporary.
448   crazy_callback_t* callback = new crazy_callback_t();
449   *callback = *callback_request;
450
451   LOG_INFO("%s: Calling back to java with handler %p, opaque %p",
452            __FUNCTION__, callback->handler, callback->opaque);
453
454   jlong arg = static_cast<jlong>(reinterpret_cast<intptr_t>(callback));
455   env->CallStaticVoidMethod(
456       s_java_callback_bindings.clazz, s_java_callback_bindings.method_id, arg);
457
458   // Back out and return false if we encounter a JNI exception.
459   if (env->ExceptionCheck() == JNI_TRUE) {
460     env->ExceptionDescribe();
461     env->ExceptionClear();
462     delete callback;
463     return false;
464   }
465
466   return true;
467 }
468
469 jboolean CreateSharedRelro(JNIEnv* env,
470                            jclass clazz,
471                            jstring library_name,
472                            jlong load_address,
473                            jobject lib_info_obj) {
474   String lib_name(env, library_name);
475
476   LOG_INFO("%s: Called for %s", __FUNCTION__, lib_name.c_str());
477
478   if (!IsValidAddress(load_address)) {
479     LOG_ERROR("%s: Invalid address 0x%llx", __FUNCTION__, load_address);
480     return false;
481   }
482
483   ScopedLibrary library;
484   if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
485     LOG_ERROR("%s: Could not find %s", __FUNCTION__, lib_name.c_str());
486     return false;
487   }
488
489   crazy_context_t* context = GetCrazyContext();
490   size_t relro_start = 0;
491   size_t relro_size = 0;
492   int relro_fd = -1;
493
494   if (!crazy_library_create_shared_relro(library.Get(),
495                                          context,
496                                          static_cast<size_t>(load_address),
497                                          &relro_start,
498                                          &relro_size,
499                                          &relro_fd)) {
500     LOG_ERROR("%s: Could not create shared RELRO sharing for %s: %s\n",
501               __FUNCTION__,
502               lib_name.c_str(),
503               crazy_context_get_error(context));
504     return false;
505   }
506
507   s_lib_info_fields.SetRelroInfo(
508       env, lib_info_obj, relro_start, relro_size, relro_fd);
509   return true;
510 }
511
512 jboolean UseSharedRelro(JNIEnv* env,
513                         jclass clazz,
514                         jstring library_name,
515                         jobject lib_info_obj) {
516   String lib_name(env, library_name);
517
518   LOG_INFO("%s: called for %s, lib_info_ref=%p",
519            __FUNCTION__,
520            lib_name.c_str(),
521            lib_info_obj);
522
523   ScopedLibrary library;
524   if (!crazy_library_find_by_name(lib_name.c_str(), library.GetPtr())) {
525     LOG_ERROR("%s: Could not find %s", __FUNCTION__, lib_name.c_str());
526     return false;
527   }
528
529   crazy_context_t* context = GetCrazyContext();
530   size_t relro_start = 0;
531   size_t relro_size = 0;
532   int relro_fd = -1;
533   s_lib_info_fields.GetRelroInfo(
534       env, lib_info_obj, &relro_start, &relro_size, &relro_fd);
535
536   LOG_INFO("%s: library=%s relro start=%p size=%p fd=%d",
537            __FUNCTION__,
538            lib_name.c_str(),
539            (void*)relro_start,
540            (void*)relro_size,
541            relro_fd);
542
543   if (!crazy_library_use_shared_relro(
544            library.Get(), context, relro_start, relro_size, relro_fd)) {
545     LOG_ERROR("%s: Could not use shared RELRO for %s: %s",
546               __FUNCTION__,
547               lib_name.c_str(),
548               crazy_context_get_error(context));
549     return false;
550   }
551
552   LOG_INFO("%s: Library %s using shared RELRO section!",
553            __FUNCTION__,
554            lib_name.c_str());
555
556   return true;
557 }
558
559 jboolean CanUseSharedRelro(JNIEnv* env, jclass clazz) {
560   return crazy_system_can_share_relro();
561 }
562
563 jlong GetPageSize(JNIEnv* env, jclass clazz) {
564   jlong result = static_cast<jlong>(sysconf(_SC_PAGESIZE));
565   LOG_INFO("%s: System page size is %lld bytes\n", __FUNCTION__, result);
566   return result;
567 }
568
569 const JNINativeMethod kNativeMethods[] = {
570     {"nativeLoadLibrary",
571      "("
572      "Ljava/lang/String;"
573      "J"
574      "Lorg/chromium/base/library_loader/Linker$LibInfo;"
575      ")"
576      "Z",
577      reinterpret_cast<void*>(&LoadLibrary)},
578     {"nativeLoadLibraryInZipFile",
579      "("
580      "Ljava/lang/String;"
581      "Ljava/lang/String;"
582      "J"
583      "Lorg/chromium/base/library_loader/Linker$LibInfo;"
584      ")"
585      "Z",
586      reinterpret_cast<void*>(&LoadLibraryInZipFile)},
587     {"nativeRunCallbackOnUiThread",
588      "("
589      "J"
590      ")"
591      "V",
592      reinterpret_cast<void*>(&RunCallbackOnUiThread)},
593     {"nativeCreateSharedRelro",
594      "("
595      "Ljava/lang/String;"
596      "J"
597      "Lorg/chromium/base/library_loader/Linker$LibInfo;"
598      ")"
599      "Z",
600      reinterpret_cast<void*>(&CreateSharedRelro)},
601     {"nativeUseSharedRelro",
602      "("
603      "Ljava/lang/String;"
604      "Lorg/chromium/base/library_loader/Linker$LibInfo;"
605      ")"
606      "Z",
607      reinterpret_cast<void*>(&UseSharedRelro)},
608     {"nativeCanUseSharedRelro",
609      "("
610      ")"
611      "Z",
612      reinterpret_cast<void*>(&CanUseSharedRelro)},
613     {"nativeGetPageSize",
614      "("
615      ")"
616      "J",
617      reinterpret_cast<void*>(&GetPageSize)}, };
618
619 }  // namespace
620
621 // JNI_OnLoad() hook called when the linker library is loaded through
622 // the regular System.LoadLibrary) API. This shall save the Java VM
623 // handle and initialize LibInfo fields.
624 jint JNI_OnLoad(JavaVM* vm, void* reserved) {
625   LOG_INFO("%s: Entering", __FUNCTION__);
626   // Get new JNIEnv
627   JNIEnv* env;
628   if (JNI_OK != vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4)) {
629     LOG_ERROR("Could not create JNIEnv");
630     return -1;
631   }
632
633   // Register native methods.
634   jclass linker_class;
635   if (!InitClassReference(env,
636                           "org/chromium/base/library_loader/Linker",
637                           &linker_class))
638     return -1;
639
640   LOG_INFO("%s: Registering native methods", __FUNCTION__);
641   env->RegisterNatives(linker_class,
642                        kNativeMethods,
643                        sizeof(kNativeMethods) / sizeof(kNativeMethods[0]));
644
645   // Find LibInfo field ids.
646   LOG_INFO("%s: Caching field IDs", __FUNCTION__);
647   if (!s_lib_info_fields.Init(env)) {
648     return -1;
649   }
650
651   // Resolve and save the Java side Linker callback class and method.
652   LOG_INFO("%s: Resolving callback bindings", __FUNCTION__);
653   if (!s_java_callback_bindings.Init(env, linker_class)) {
654     return -1;
655   }
656
657   // Save JavaVM* handle into context.
658   crazy_context_t* context = GetCrazyContext();
659   crazy_context_set_java_vm(context, vm, JNI_VERSION_1_4);
660
661   // Register the function that the crazy linker can call to post code
662   // for later execution.
663   crazy_context_set_callback_poster(context, &PostForLaterExecution, NULL);
664
665   LOG_INFO("%s: Done", __FUNCTION__);
666   return JNI_VERSION_1_4;
667 }