summaryrefslogtreecommitdiffstats
path: root/base/android
diff options
context:
space:
mode:
authorsimonb@chromium.org <simonb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-13 13:26:43 +0000
committersimonb@chromium.org <simonb@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-05-13 13:26:43 +0000
commit284ae62aa0ed0f836876ce4cc2a3824b7896bf0b (patch)
tree320de6997570e5cca6ae6bbe382a899d17f5bc27 /base/android
parent43ecd96d62c3a2bfe062ce840a0a0b8b0d674136 (diff)
downloadchromium_src-284ae62aa0ed0f836876ce4cc2a3824b7896bf0b.zip
chromium_src-284ae62aa0ed0f836876ce4cc2a3824b7896bf0b.tar.gz
chromium_src-284ae62aa0ed0f836876ce4cc2a3824b7896bf0b.tar.bz2
Crazy linker fix for
https://code.google.com/p/chromium/issues/detail?id=355595 There is a race between the crazy linker running on the native thread and the system linker running at the same time on the main UI thread, It can show up in several places, but in its simplest incarnation appears as: --- crazy linker on native thread mprotect page writable --- system linker on main thread mprotect page writable write it mprotect page readonly --- crazy linker on native thread write it [ <- sigsegv, page readonly (code=2) ] mprotect page readonly [ not reached ] The patch moves the crazy linker's r_map update calls so that they execute on the main UI thread. This ensures that the crazy linker sets these pages writable and then readonly once done synchronously with system linker activity. Companion to: https://googleplex-android-review.git.corp.google.com/#/c/448556/ BUG=355595 Review URL: https://codereview.chromium.org/228023005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@270084 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/android')
-rw-r--r--base/android/java/src/org/chromium/base/library_loader/Linker.java24
-rw-r--r--base/android/linker/linker_jni.cc173
2 files changed, 170 insertions, 27 deletions
diff --git a/base/android/java/src/org/chromium/base/library_loader/Linker.java b/base/android/java/src/org/chromium/base/library_loader/Linker.java
index e17ba6d..54a672d 100644
--- a/base/android/java/src/org/chromium/base/library_loader/Linker.java
+++ b/base/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -11,6 +11,7 @@ import android.os.Parcelable;
import android.util.Log;
import org.chromium.base.SysUtils;
+import org.chromium.base.ThreadUtils;
import java.io.File;
import java.io.FileInputStream;
@@ -842,6 +843,29 @@ public class Linker {
}
/**
+ * Move activity from the native thread to the main UI thread.
+ * Called from native code on its own thread. Posts a callback from
+ * the UI thread back to native code.
+ *
+ * @param opaque Opaque argument.
+ */
+ public static void postCallbackOnMainThread(final long opaque) {
+ ThreadUtils.postOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ nativeRunCallbackOnUiThread(opaque);
+ }
+ });
+ }
+
+ /**
+ * Native method to run callbacks on the main UI thread.
+ * Supplied by the crazy linker and called by postCallbackOnMainThread.
+ * @param opaque Opaque crazy linker arguments.
+ */
+ private static native void nativeRunCallbackOnUiThread(long opaque);
+
+ /**
* Native method used to load a library.
* @param library Platform specific library name (e.g. libfoo.so)
* @param loadAddress Explicit load address, or 0 for randomized one.
diff --git a/base/android/linker/linker_jni.cc b/base/android/linker/linker_jni.cc
index 0222e49..46aa45d 100644
--- a/base/android/linker/linker_jni.cc
+++ b/base/android/linker/linker_jni.cc
@@ -71,31 +71,6 @@ String::String(JNIEnv* env, jstring str) {
env->ReleaseStringUTFChars(str, bytes);
}
-// A scoped crazy_library_t that automatically closes the handle
-// on scope exit, unless Release() has been called.
-class ScopedLibrary {
- public:
- ScopedLibrary() : lib_(NULL) {}
-
- ~ScopedLibrary() {
- if (lib_)
- crazy_library_close(lib_);
- }
-
- crazy_library_t* Get() { return lib_; }
-
- crazy_library_t** GetPtr() { return &lib_; }
-
- crazy_library_t* Release() {
- crazy_library_t* ret = lib_;
- lib_ = NULL;
- return ret;
- }
-
- private:
- crazy_library_t* lib_;
-};
-
// Return a pointer to the base name from an input |path| string.
const char* GetBaseNamePtr(const char* path) {
const char* p = strrchr(path, '/');
@@ -140,6 +115,25 @@ bool InitFieldId(JNIEnv* env,
return true;
}
+// Initialize a jmethodID corresponding to the static method of a given
+// |clazz|, with name |method_name| and signature |method_sig|.
+// |env| is the current JNI environment handle.
+// On success, return true and set |*method_id|.
+bool InitStaticMethodId(JNIEnv* env,
+ jclass clazz,
+ const char* method_name,
+ const char* method_sig,
+ jmethodID* method_id) {
+ *method_id = env->GetStaticMethodID(clazz, method_name, method_sig);
+ if (!*method_id) {
+ LOG_ERROR("Could not find ID for static method '%s'", method_name);
+ return false;
+ }
+ LOG_INFO("%s: Found ID %p for static method '%s'",
+ __FUNCTION__, *method_id, method_name);
+ return true;
+}
+
// A class used to model the field IDs of the org.chromium.base.Linker
// LibInfo inner class, used to communicate data with the Java side
// of the linker.
@@ -221,6 +215,31 @@ crazy_context_t* GetCrazyContext() {
return s_crazy_context;
}
+// A scoped crazy_library_t that automatically closes the handle
+// on scope exit, unless Release() has been called.
+class ScopedLibrary {
+ public:
+ ScopedLibrary() : lib_(NULL) {}
+
+ ~ScopedLibrary() {
+ if (lib_)
+ crazy_library_close_with_context(lib_, GetCrazyContext());
+ }
+
+ crazy_library_t* Get() { return lib_; }
+
+ crazy_library_t** GetPtr() { return &lib_; }
+
+ crazy_library_t* Release() {
+ crazy_library_t* ret = lib_;
+ lib_ = NULL;
+ return ret;
+ }
+
+ private:
+ crazy_library_t* lib_;
+};
+
// Load a library with the chromium linker. This will also call its
// JNI_OnLoad() method, which shall register its methods. Note that
// lazy native method resolution will _not_ work after this, because
@@ -282,6 +301,89 @@ jboolean LoadLibrary(JNIEnv* env,
return true;
}
+// Class holding the Java class and method ID for the Java side Linker
+// postCallbackOnMainThread method.
+struct JavaCallbackBindings_class {
+ jclass clazz;
+ jmethodID method_id;
+
+ // Initialize an instance.
+ bool Init(JNIEnv* env, jclass linker_class) {
+ clazz = reinterpret_cast<jclass>(env->NewGlobalRef(linker_class));
+ return InitStaticMethodId(env,
+ linker_class,
+ "postCallbackOnMainThread",
+ "(J)V",
+ &method_id);
+ }
+};
+
+static JavaCallbackBindings_class s_java_callback_bindings;
+
+// Designated receiver function for callbacks from Java. Its name is known
+// to the Java side.
+// |env| is the current JNI environment handle and is ignored here.
+// |clazz| is the static class handle for org.chromium.base.Linker,
+// and is ignored here.
+// |arg| is a pointer to an allocated crazy_callback_t, deleted after use.
+void RunCallbackOnUiThread(JNIEnv* env, jclass clazz, jlong arg) {
+ crazy_callback_t* callback = reinterpret_cast<crazy_callback_t*>(arg);
+
+ LOG_INFO("%s: Called back from java with handler %p, opaque %p",
+ __FUNCTION__, callback->handler, callback->opaque);
+
+ crazy_callback_run(callback);
+ delete callback;
+}
+
+// Request a callback from Java. The supplied crazy_callback_t is valid only
+// for the duration of this call, so we copy it to a newly allocated
+// crazy_callback_t and then call the Java side's postCallbackOnMainThread.
+// This will call back to to our RunCallbackOnUiThread some time
+// later on the UI thread.
+// |callback_request| is a crazy_callback_t.
+// |poster_opaque| is unused.
+// Returns true if the callback request succeeds.
+static bool PostForLaterExecution(crazy_callback_t* callback_request,
+ void* poster_opaque UNUSED) {
+ crazy_context_t* context = GetCrazyContext();
+
+ JavaVM* vm;
+ int minimum_jni_version;
+ crazy_context_get_java_vm(context,
+ reinterpret_cast<void**>(&vm),
+ &minimum_jni_version);
+
+ // Do not reuse JNIEnv from JNI_OnLoad, but retrieve our own.
+ JNIEnv* env;
+ if (JNI_OK != vm->GetEnv(
+ reinterpret_cast<void**>(&env), minimum_jni_version)) {
+ LOG_ERROR("Could not create JNIEnv");
+ return false;
+ }
+
+ // Copy the callback; the one passed as an argument may be temporary.
+ crazy_callback_t* callback = new crazy_callback_t();
+ *callback = *callback_request;
+
+ LOG_INFO("%s: Calling back to java with handler %p, opaque %p",
+ __FUNCTION__, callback->handler, callback->opaque);
+
+ jlong arg = static_cast<jlong>(reinterpret_cast<intptr_t>(callback));
+ env->CallStaticVoidMethod(
+ s_java_callback_bindings.clazz, s_java_callback_bindings.method_id, arg);
+
+ // Back out and return false if we encounter a JNI exception.
+ if (env->ExceptionCheck() == JNI_TRUE) {
+ env->ExceptionDescribe();
+ env->ExceptionClear();
+ delete callback;
+ return false;
+ }
+
+ return true;
+}
+
jboolean CreateSharedRelro(JNIEnv* env,
jclass clazz,
jstring library_name,
@@ -391,6 +493,12 @@ const JNINativeMethod kNativeMethods[] = {
")"
"Z",
reinterpret_cast<void*>(&LoadLibrary)},
+ {"nativeRunCallbackOnUiThread",
+ "("
+ "J"
+ ")"
+ "V",
+ reinterpret_cast<void*>(&RunCallbackOnUiThread)},
{"nativeCreateSharedRelro",
"("
"Ljava/lang/String;"
@@ -433,8 +541,9 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
// Register native methods.
jclass linker_class;
- if (!InitClassReference(
- env, "org/chromium/base/library_loader/Linker", &linker_class))
+ if (!InitClassReference(env,
+ "org/chromium/base/library_loader/Linker",
+ &linker_class))
return -1;
LOG_INFO("%s: Registering native methods", __FUNCTION__);
@@ -448,10 +557,20 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
return -1;
}
+ // Resolve and save the Java side Linker callback class and method.
+ LOG_INFO("%s: Resolving callback bindings", __FUNCTION__);
+ if (!s_java_callback_bindings.Init(env, linker_class)) {
+ return -1;
+ }
+
// Save JavaVM* handle into context.
crazy_context_t* context = GetCrazyContext();
crazy_context_set_java_vm(context, vm, JNI_VERSION_1_4);
+ // Register the function that the crazy linker can call to post code
+ // for later execution.
+ crazy_context_set_callback_poster(context, &PostForLaterExecution, NULL);
+
LOG_INFO("%s: Done", __FUNCTION__);
return JNI_VERSION_1_4;
}