Generating stub code for registering a jni function at runtime.
On ARM and x86. Added a unit test.
Change-Id: I6b1ee09ad18295108f406ce21d73555796ecbba6
diff --git a/src/jni_compiler.cc b/src/jni_compiler.cc
index bb1e965..9da1ab0 100644
--- a/src/jni_compiler.cc
+++ b/src/jni_compiler.cc
@@ -17,7 +17,15 @@
namespace art {
-JniCompiler::JniCompiler(InstructionSet insns) {
+namespace arm {
+ByteArray* CreateJniStub();
+}
+
+namespace x86 {
+ByteArray* CreateJniStub();
+}
+
+JniCompiler::JniCompiler(InstructionSet insns) : jni_stub_(NULL) {
if (insns == kThumb2) {
// currently only ARM code generation is supported
instruction_set_ = kArm;
@@ -60,6 +68,17 @@
// Cache of IsStatic as we call it often enough
const bool is_static = native_method->IsStatic();
+ // 0. native_method->RegisterNative(jni_stub_ stuff). Note that jni_stub_ will invoke dlsym.
+ if (jni_stub_ == NULL) {
+ if (instruction_set_ == kArm) {
+ jni_stub_ = arm::CreateJniStub();
+ } else {
+ jni_stub_ = x86::CreateJniStub();
+ }
+ }
+ native_method->RegisterNative(jni_stub_->GetData());
+ // TODO: Need to make sure that the stub is copied into the image. I.e.,
+ // ByteArray* needs to be reachable either as a root or from the object graph.
// 1. Build the frame
const size_t frame_size(jni_conv->FrameSize());
diff --git a/src/jni_compiler.h b/src/jni_compiler.h
index ec2e23c..25514b9 100644
--- a/src/jni_compiler.h
+++ b/src/jni_compiler.h
@@ -5,6 +5,7 @@
#include "constants.h"
#include "macros.h"
+#include "object.h"
namespace art {
@@ -37,6 +38,8 @@
InstructionSet instruction_set_;
+ ByteArray* jni_stub_; // Stub to perform native method symbol lookup
+
DISALLOW_COPY_AND_ASSIGN(JniCompiler);
};
diff --git a/src/jni_compiler_test.cc b/src/jni_compiler_test.cc
index 343ea1d..216350b 100644
--- a/src/jni_compiler_test.cc
+++ b/src/jni_compiler_test.cc
@@ -52,8 +52,10 @@
}
ASSERT_TRUE(jmethod_ != NULL);
- JNINativeMethod methods[] = {{method_name, method_sig, native_fnptr}};
- ASSERT_EQ(JNI_OK, env_->RegisterNatives(jklass_, methods, 1));
+ if (native_fnptr) {
+ JNINativeMethod methods[] = {{method_name, method_sig, native_fnptr}};
+ ASSERT_EQ(JNI_OK, env_->RegisterNatives(jklass_, methods, 1));
+ }
jmethodID constructor = env_->GetMethodID(jklass_, "<init>", "()V");
jobj_ = env_->NewObject(jklass_, constructor);
@@ -92,6 +94,22 @@
EXPECT_EQ(2, gJava_MyClass_foo_calls);
}
+TEST_F(JniCompilerTest, CompileAndRunIntMethodThroughStub) {
+ SetupForTest(false,
+ "bar",
+ "(I)I",
+ NULL /* dlsym will find &Java_MyClass_bar later */);
+
+ std::string path("libarttest.so");
+ std::string reason;
+ ASSERT_TRUE(Runtime::Current()->GetJavaVM()->LoadNativeLibrary(
+ path, const_cast<ClassLoader*>(class_loader_), reason))
+ << path << ": " << reason;
+
+ jint result = env_->CallNonvirtualIntMethod(jobj_, jklass_, jmethod_, 24);
+ EXPECT_EQ(25, result);
+}
+
int gJava_MyClass_fooI_calls = 0;
jint Java_MyClass_fooI(JNIEnv* env, jobject thisObj, jint x) {
EXPECT_EQ(1u, Thread::Current()->NumSirtReferences());
diff --git a/src/jni_internal.cc b/src/jni_internal.cc
index 83a147f..954fafd 100644
--- a/src/jni_internal.cc
+++ b/src/jni_internal.cc
@@ -25,6 +25,25 @@
namespace art {
+void* FindNativeMethod(Thread* thread) {
+ DCHECK(Thread::Current() == thread);
+
+ Method* method = const_cast<Method*>(thread->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);
+ if (native_code == NULL) {
+ DCHECK(thread->IsExceptionPending());
+ return NULL;
+ } else {
+ // Register so that future calls don't come here
+ method->RegisterNative(native_code);
+ return native_code;
+ }
+}
+
/*
* Add a local reference for an object to the current stack frame. When
* the native function returns, the reference will be discarded.
@@ -452,6 +471,7 @@
SharedLibrary(const std::string& path, void* handle, Object* class_loader)
: path_(path),
handle_(handle),
+ class_loader_(class_loader),
jni_on_load_lock_("JNI_OnLoad lock"),
jni_on_load_thread_id_(Thread::Current()->GetThinLockId()),
jni_on_load_result_(kPending) {
diff --git a/src/jni_internal.h b/src/jni_internal.h
index a723b32..20aa716 100644
--- a/src/jni_internal.h
+++ b/src/jni_internal.h
@@ -22,6 +22,7 @@
class Thread;
void JniAbort(const char* jni_function_name);
+void* FindNativeMethod(Thread* thread);
template<typename T> T Decode(JNIEnv*, jobject);
template<typename T> T AddLocalReference(JNIEnv*, const Object*);
diff --git a/src/jni_tests.cc b/src/jni_tests.cc
new file mode 100644
index 0000000..934ccac
--- /dev/null
+++ b/src/jni_tests.cc
@@ -0,0 +1,9 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "jni.h"
+#include <stdio.h>
+
+extern "C"
+JNIEXPORT jint JNICALL Java_MyClass_bar(JNIEnv* env, jobject thisObj, jint count) {
+ return count + 1;
+}
diff --git a/src/stub_arm.cc b/src/stub_arm.cc
new file mode 100644
index 0000000..2e2f6a6
--- /dev/null
+++ b/src/stub_arm.cc
@@ -0,0 +1,56 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "assembler_arm.h"
+#include "jni_internal.h"
+#include "object.h"
+
+#define __ assembler->
+
+namespace art {
+namespace arm {
+
+ByteArray* CreateJniStub() {
+ UniquePtr<ArmAssembler> assembler( static_cast<ArmAssembler*>(Assembler::Create(kArm)) );
+
+ RegList save = (1 << R0) | (1 << R1) | (1 << R2) | (1 << R3) | (1 << LR);
+
+ // Build frame and save registers. Save 5 registers.
+ __ PushList(save);
+ // Ensure 16-byte alignment
+ __ AddConstant(SP, -12);
+
+ // Pass Thread::Current() in R0
+ __ mov(R0, ShifterOperand(R9));
+
+ // Call FindNativeMethod
+ __ LoadImmediate(R12, reinterpret_cast<int32_t>(&FindNativeMethod));
+ __ blx(R12);
+
+ // Save result of FindNativeMethod in R12
+ __ mov(R12, ShifterOperand(R0));
+
+ // Restore registers (including outgoing arguments)
+ __ AddConstant(SP, 12);
+ __ PopList(save);
+
+ __ cmp(R12, ShifterOperand(0));
+
+ // If R12 != 0 tail call into native code
+ __ mov(PC, ShifterOperand(R12), NE);
+
+ // Return to caller to handle exception
+ __ mov(PC, ShifterOperand(LR));
+
+ assembler->EmitSlowPaths();
+
+ size_t cs = assembler->CodeSize();
+ ByteArray* jni_stub = ByteArray::Alloc(cs);
+ CHECK(jni_stub != NULL);
+ MemoryRegion code(jni_stub->GetData(), jni_stub->GetLength());
+ assembler->FinalizeInstructions(code);
+
+ return jni_stub;
+}
+
+} // namespace arm
+} // namespace art
diff --git a/src/stub_x86.cc b/src/stub_x86.cc
new file mode 100644
index 0000000..bddfc6c
--- /dev/null
+++ b/src/stub_x86.cc
@@ -0,0 +1,47 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+#include "assembler_x86.h"
+#include "jni_internal.h"
+#include "object.h"
+
+#define __ assembler->
+
+namespace art {
+namespace x86 {
+
+ByteArray* CreateJniStub() {
+ UniquePtr<X86Assembler> assembler( static_cast<X86Assembler*>(Assembler::Create(kX86)) );
+
+ // Pad stack to ensure 16-byte alignment
+ __ pushl(Immediate(0));
+ __ pushl(Immediate(0));
+ __ pushl(Immediate(0));
+ __ fs()->movl(ECX, Address::Absolute(Thread::SelfOffset()));
+ __ pushl(ECX); // Thread*
+
+ __ Call(reinterpret_cast<int32_t>(&FindNativeMethod), X86ManagedRegister::FromCpuRegister(ECX));
+
+ __ addl(ESP, Immediate(16));
+
+ Label no_native_code_found; // forward declaration
+ __ cmpl(EAX, Immediate(0));
+ __ j(kEqual, &no_native_code_found);
+
+ __ jmp(EAX); // Tail call into native code
+
+ __ Bind(&no_native_code_found);
+ __ ret(); // return to caller to handle exception
+
+ assembler->EmitSlowPaths();
+
+ size_t cs = assembler->CodeSize();
+ ByteArray* jni_stub = ByteArray::Alloc(cs);
+ CHECK(jni_stub != NULL);
+ MemoryRegion code(jni_stub->GetData(), jni_stub->GetLength());
+ assembler->FinalizeInstructions(code);
+
+ return jni_stub;
+}
+
+} // namespace x86
+} // namespace art