SIRT work around JNI support for legacy apps
Legacy apps may be broken and expect a jobject to equal an Object*. This
is a work around to support outgoing SIRT arguments being converted to
be Object*s. It steals the unused GC map pointer on native methods.
Change-Id: I8fb3701b0cdf15be9e8f9b722e47386505482666
diff --git a/src/dalvik_system_VMRuntime.cc b/src/dalvik_system_VMRuntime.cc
index 6f9480d..10732db 100644
--- a/src/dalvik_system_VMRuntime.cc
+++ b/src/dalvik_system_VMRuntime.cc
@@ -125,10 +125,10 @@
// Note that targetSdkVersion may be CUR_DEVELOPMENT (10000).
// Note that targetSdkVersion may be 0, meaning "current".
if (targetSdkVersion > 0 && targetSdkVersion <= 13 /* honeycomb-mr2 */) {
+ JNIEnvExt* env_ext = reinterpret_cast<JNIEnvExt*>(env);
// TODO: running with CheckJNI should override this and force you to obey the strictest rules.
LOG(INFO) << "Turning on JNI app bug workarounds for target SDK version " << targetSdkVersion << "...";
- // Runtime::Current()->GetJavaVM()->work_around_app_jni_bugs = true;
- UNIMPLEMENTED(WARNING) << "Support work arounds for app JNI bugs";
+ env_ext->vm->work_around_app_jni_bugs = true;
}
}
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 8b61226..d0a6776 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -2252,7 +2252,7 @@
VLOG(jni) << "[Registering JNI native method " << PrettyMethod(m) << "]";
- m->RegisterNative(methods[i].fnPtr);
+ m->RegisterNative(ts.Self(), methods[i].fnPtr);
}
return JNI_OK;
}
diff --git a/src/object.cc b/src/object.cc
index 3aba5c8..cf15a64 100644
--- a/src/object.cc
+++ b/src/object.cc
@@ -34,6 +34,7 @@
#include "monitor.h"
#include "object_utils.h"
#include "runtime.h"
+#include "runtime_support.h"
#include "stack.h"
#include "utils.h"
@@ -598,17 +599,34 @@
return native_method != jni_stub;
}
-void Method::RegisterNative(const void* native_method) {
+void Method::RegisterNative(Thread* self, const void* native_method) {
+ DCHECK(Thread::Current() == self);
CHECK(IsNative()) << PrettyMethod(this);
CHECK(native_method != NULL) << PrettyMethod(this);
- SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_),
- native_method, false);
+ if (!self->GetJniEnv()->vm->work_around_app_jni_bugs) {
+ SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_),
+ native_method, false);
+ } else {
+ // We've been asked to associate this method with the given native method but are working
+ // around JNI bugs, that include not giving Object** SIRT references to native methods. Direct
+ // the native method to runtime support and store the target somewhere runtime support will
+ // find it.
+#if defined(__arm__)
+ SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_),
+ reinterpret_cast<const void*>(art_work_around_app_jni_bugs), false);
+#else
+ UNIMPLEMENTED(FATAL);
+#endif
+ SetFieldPtr<const uint8_t*>(OFFSET_OF_OBJECT_MEMBER(Method, gc_map_),
+ reinterpret_cast<const uint8_t*>(native_method), false);
+ }
}
void Method::UnregisterNative() {
CHECK(IsNative()) << PrettyMethod(this);
// restore stub to lookup native pointer via dlsym
- RegisterNative(Runtime::Current()->GetJniDlsymLookupStub()->GetData());
+ SetFieldPtr<const void*>(OFFSET_OF_OBJECT_MEMBER(Method, native_method_),
+ Runtime::Current()->GetJniDlsymLookupStub()->GetData(), false);
}
void Class::SetStatus(Status new_status) {
diff --git a/src/object.h b/src/object.h
index f9c8eb7..da8064f 100644
--- a/src/object.h
+++ b/src/object.h
@@ -762,7 +762,7 @@
bool IsRegistered() const;
- void RegisterNative(const void* native_method);
+ void RegisterNative(Thread* self, const void* native_method);
void UnregisterNative();
diff --git a/src/runtime_support.cc b/src/runtime_support.cc
index f158680..d9d7e90 100644
--- a/src/runtime_support.cc
+++ b/src/runtime_support.cc
@@ -131,21 +131,21 @@
return thread->DecodeJObject(obj);
}
-extern void* FindNativeMethod(Thread* thread) {
- DCHECK(Thread::Current() == thread);
+extern void* FindNativeMethod(Thread* self) {
+ DCHECK(Thread::Current() == self);
- Method* method = const_cast<Method*>(thread->GetCurrentMethod());
+ Method* method = const_cast<Method*>(self->GetCurrentMethod());
DCHECK(method != NULL);
// Lookup symbol address for method, on failure we'll return NULL with an
// exception set, otherwise we return the address of the method we found.
- void* native_code = thread->GetJniEnv()->vm->FindCodeForNativeMethod(method);
+ void* native_code = self->GetJniEnv()->vm->FindCodeForNativeMethod(method);
if (native_code == NULL) {
- DCHECK(thread->IsExceptionPending());
+ DCHECK(self->IsExceptionPending());
return NULL;
} else {
// Register so that future calls don't come here
- method->RegisterNative(native_code);
+ method->RegisterNative(self, native_code);
return native_code;
}
}
@@ -510,6 +510,74 @@
return code;
}
+static void WorkAroundJniBugsForJobject(intptr_t* arg_ptr) {
+ intptr_t value = *arg_ptr;
+ Object** value_as_jni_rep = reinterpret_cast<Object**>(value);
+ Object* value_as_work_around_rep = value_as_jni_rep != NULL ? *value_as_jni_rep : NULL;
+ CHECK(Heap::IsHeapAddress(value_as_work_around_rep));
+ *arg_ptr = reinterpret_cast<intptr_t>(value_as_work_around_rep);
+}
+
+extern "C" const void* artWorkAroundAppJniBugs(Thread* self, intptr_t* sp) {
+ DCHECK(Thread::Current() == self);
+ // TODO: this code is specific to ARM
+ // On entry the stack pointed by sp is:
+ // | arg3 | <- Calling JNI method's frame (and extra bit for out args)
+ // | LR |
+ // | R3 | arg2
+ // | R2 | arg1
+ // | R1 | jclass/jobject
+ // | R0 | JNIEnv
+ // | unused |
+ // | unused |
+ // | unused | <- sp
+ Method* jni_method = self->GetTopOfStack().GetMethod();
+ DCHECK(jni_method->IsNative()) << PrettyMethod(jni_method);
+ intptr_t* arg_ptr = sp + 4; // pointer to r1 on stack
+ // Fix up this/jclass argument
+ WorkAroundJniBugsForJobject(arg_ptr);
+ arg_ptr++;
+ // Fix up jobject arguments
+ MethodHelper mh(jni_method);
+ int reg_num = 2; // Current register being processed, -1 for stack arguments.
+ for (int32_t i = 1; i < mh.GetShortyLength(); i++) {
+ char shorty_char = mh.GetShorty()[i];
+ if (shorty_char == 'L') {
+ WorkAroundJniBugsForJobject(arg_ptr);
+ }
+ if (shorty_char == 'J' || shorty_char == 'D') {
+ if (reg_num == 2) {
+ arg_ptr = sp + 8; // skip to out arguments
+ reg_num = -1;
+ } else if (reg_num == 3) {
+ arg_ptr = sp + 10; // skip to out arguments plus 2 slots as long must be aligned
+ reg_num = -1;
+ } else {
+ DCHECK(reg_num == -1);
+ if ((reinterpret_cast<intptr_t>(arg_ptr) & 7) == 4) {
+ arg_ptr += 3; // unaligned, pad and move through stack arguments
+ } else {
+ arg_ptr += 2; // aligned, move through stack arguments
+ }
+ }
+ } else {
+ if (reg_num == 2) {
+ arg_ptr++; // move through register arguments
+ reg_num++;
+ } else if (reg_num == 3) {
+ arg_ptr = sp + 8; // skip to outgoing stack arguments
+ reg_num = -1;
+ } else {
+ DCHECK(reg_num == -1);
+ arg_ptr++; // move through stack arguments
+ }
+ }
+ }
+ // Load expected destination, see Method::RegisterNative
+ return reinterpret_cast<const void*>(jni_method->GetGcMapRaw());
+}
+
+
// Fast path field resolution that can't throw exceptions
static Field* FindFieldFast(uint32_t field_idx, const Method* referrer, bool is_primitive,
size_t expected_size) {
diff --git a/src/runtime_support.h b/src/runtime_support.h
index 305bbf6..97197e6 100644
--- a/src/runtime_support.h
+++ b/src/runtime_support.h
@@ -99,6 +99,7 @@
extern "C" void* art_resolve_string_from_code(void*, uint32_t);
extern "C" void* art_resolve_method_from_code(void* referrer, uint32_t method_idx, bool is_direct);
extern "C" void art_update_debugger(void*, void*, int32_t, void*);
+ extern "C" void art_work_around_app_jni_bugs();
/* Conversions */
extern "C" float __aeabi_i2f(int op1); // OP_INT_TO_FLOAT
diff --git a/src/runtime_support_arm.S b/src/runtime_support_arm.S
index b337ebc..efa6ef8 100644
--- a/src/runtime_support_arm.S
+++ b/src/runtime_support_arm.S
@@ -210,6 +210,23 @@
INVOKE_TRAMPOLINE art_invoke_super_trampoline_with_access_check, artInvokeSuperTrampolineWithAccessCheck
INVOKE_TRAMPOLINE art_invoke_virtual_trampoline_with_access_check, artInvokeVirtualTrampolineWithAccessCheck
+ .global art_work_around_app_jni_bugs
+ .extern artWorkAroundAppJniBugs
+ /*
+ * Entry point of native methods when JNI bug compatibility is enabled.
+ */
+art_work_around_app_jni_bugs:
+ @ save registers that may contain arguments and LR that will be crushed by a call
+ push {r0-r3, lr}
+ sub sp, #12 @ 3 words of space for alignment
+ mov r0, r9 @ pass Thread::Current
+ mov r1, sp @ pass SP
+ bl artWorkAroundAppJniBugs @ (Thread*, SP)
+ add sp, #12 @ rewind stack
+ mov r12, r0 @ save target address
+ pop {r0-r3, lr} @ restore possibly modified argument registers
+ bx r12 @ tail call into JNI routine
+
.global art_handle_fill_data_from_code
.extern artHandleFillArrayDataFromCode
/*