We can't decide at compile-time whether called method is JNI. That's because methods declared as native in one class can be overriden in subclass and be non-native.
Solution for this is to call potential JNI methods like any other methods. Method pointers for actual JNI methods point to a trampoline which takes care of pushing pointer to JNI environment and class object reference for static methods. Signed-off-by: Tomek Grabiec <tgrab...@gmail.com> --- arch/x86/emit-code.c | 71 ++++++++++++++++---- arch/x86/insn-selector.brg | 128 +----------------------------------- include/jit/emit-code.h | 2 + include/vm/stack-trace.h | 8 +- jit/args.c | 60 ++--------------- jit/trampoline.c | 23 ++++--- regression/jvm/StackTraceTest.java | 8 +-- test/vm/stack-trace-stub.c | 7 +- vm/call.c | 56 ---------------- vm/method.c | 7 -- vm/stack-trace.c | 11 ++-- 11 files changed, 97 insertions(+), 284 deletions(-) diff --git a/arch/x86/emit-code.c b/arch/x86/emit-code.c index c4887a5..91fcf2b 100644 --- a/arch/x86/emit-code.c +++ b/arch/x86/emit-code.c @@ -1856,12 +1856,7 @@ void emit_trampoline(struct compilation_unit *cu, __emit_push_reg(buf, MACH_REG_EAX); if (method_is_virtual(cu->method)) { - /* For JNI calls 'this' pointer is in the second call - argument. */ - if (vm_method_is_jni(cu->method)) - __emit_push_membase(buf, MACH_REG_EBP, 0x0c); - else - __emit_push_membase(buf, MACH_REG_EBP, 0x08); + __emit_push_membase(buf, MACH_REG_EBP, 0x08); __emit_push_imm(buf, (unsigned long)cu); __emit_call(buf, fixup_vtable); @@ -1886,6 +1881,63 @@ static void fixup_branch_target(uint8_t *target_p, void *target) target_p[0] = cur; } +void emit_jni_trampoline(struct buffer *buf, struct vm_method *vmm, + void *target) +{ + uint8_t *jne_target; + + jit_text_lock(); + + buf->buf = jit_text_ptr(); + + /* return address is passed implicitly as last argument */ + __emit_push_imm(buf, (unsigned long) vmm); + __emit_push_reg(buf, MACH_REG_EBP); + + /* If this returns non-zero then StackOverflowError occurred. */ + __emit_call(buf, vm_enter_jni); + + /* test %eax, %eax */ + __emit_reg_reg(buf, 0x33, MACH_REG_EAX, MACH_REG_EAX); + + /* jne */ + emit(buf, 0x0f); + emit(buf, 0x85); + jne_target = buffer_current(buf); + emit_imm32(buf, 0); + + /* Cleanup call arguments and return address. */ + __emit_add_imm_reg(buf, 3 * sizeof(long), MACH_REG_ESP); + + if (vm_method_is_static(vmm)) + __emit_push_imm(buf, (unsigned long) vmm->class->object); + + __emit_push_imm(buf, (unsigned long) vm_jni_get_jni_env()); + + __emit_call(buf, target); + + /* Cleanup args. Leave one slot for return address. */ + if (vm_method_is_static(vmm)) + __emit_add_imm_reg(buf, sizeof(long), MACH_REG_ESP); + + __emit_push_reg(buf, MACH_REG_EAX); + __emit_call(buf, vm_leave_jni); + __emit_mov_reg_membase(buf, MACH_REG_EAX, MACH_REG_ESP, sizeof(long)); + __emit_pop_reg(buf, MACH_REG_EAX); + + emit_ret(buf); + + /* We will jump here if StackOverflowError occurred. */ + fixup_branch_target(jne_target, buffer_current(buf)); + + /* cleanup vm_enter_jni() call arguments. */ + __emit_add_imm_reg(buf, 2 * sizeof(long), MACH_REG_ESP); + emit_ret(buf); + + jit_text_reserve(buffer_offset(buf)); + jit_text_unlock(); +} + /* Note: a < b, always */ static void emit_itable_bsearch(struct buffer *buf, struct itable_entry **table, unsigned int a, unsigned int b) @@ -2786,12 +2838,7 @@ void emit_trampoline(struct compilation_unit *cu, __emit64_mov_imm_reg(buf, (unsigned long) cu, MACH_REG_RDI); - /* For JNI calls 'this' pointer is in the second call - argument. */ - if (vm_method_is_jni(cu->method)) - __emit64_mov_membase_reg(buf, MACH_REG_RBP, 0x18, MACH_REG_RSI); - else - __emit64_mov_membase_reg(buf, MACH_REG_RBP, 0x10, MACH_REG_RSI); + __emit64_mov_membase_reg(buf, MACH_REG_RBP, 0x10, MACH_REG_RSI); __emit64_mov_reg_reg(buf, MACH_REG_RAX, MACH_REG_RDX); __emit_call(buf, fixup_vtable); diff --git a/arch/x86/insn-selector.brg b/arch/x86/insn-selector.brg index 42041cb..01b57d7 100644 --- a/arch/x86/insn-selector.brg +++ b/arch/x86/insn-selector.brg @@ -746,10 +746,7 @@ reg: EXPR_INVOKEINTERFACE(arg) 1 /* invoke method */ call_insn = reg_insn(INSN_CALL_REG, call_target); - if (vm_method_is_jni(method)) - select_jni_call(s, tree, call_insn, method); - else - select_insn(s, tree, call_insn); + select_insn(s, tree, call_insn); nr_stack_args = get_stack_args_count(method); if (nr_stack_args) @@ -2748,120 +2745,6 @@ emulate_op_64(struct _MBState *state, struct basic_block *s, } #ifdef CONFIG_X86_32 -static void select_jni_set_call_site_addr(struct basic_block *s, - struct tree_node *tree, - struct var_info *offset_reg, - unsigned long field) -{ - select_insn(s, tree, - membase_insn(INSN_MOV_IP_THREAD_LOCAL_MEMBASE, - offset_reg, field)); -} - -static void select_jni_set_method(struct basic_block *s, - struct tree_node *tree, - struct vm_method *method, - struct var_info *offset_reg, - unsigned long field) -{ - select_insn(s, tree, imm_membase_insn(INSN_MOV_IMM_THREAD_LOCAL_MEMBASE, - (unsigned long) method, offset_reg, field)); -} -#else /* CONFIG_X86_32 */ -static void select_jni_set_call_site_addr(struct basic_block *s, - struct tree_node *tree, - struct var_info *offset_reg, - unsigned long field) -{ - struct var_info *ip_reg; - - ip_reg = get_var(s->b_parent, J_LONG); - - select_insn(s, tree, reg_insn(INSN_MOV_IP_REG, ip_reg)); - select_insn(s, tree, - reg_membase_insn(INSN_MOV_REG_THREAD_LOCAL_MEMBASE, - ip_reg, offset_reg, field)); -} - -static void select_jni_set_method(struct basic_block *s, - struct tree_node *tree, - struct vm_method *method, - struct var_info *offset_reg, - unsigned long field) -{ - struct var_info *method_reg; - - method_reg = get_var(s->b_parent, J_LONG); - - select_insn(s, tree, - imm_reg_insn(INSN_MOV_IMM_REG, - (unsigned long) method, method_reg)); - select_insn(s, tree, - reg_membase_insn(INSN_MOV_REG_THREAD_LOCAL_MEMBASE, - method_reg, offset_reg, field)); -} -#endif /* CONFIG_X86_32 */ - -static void select_jni_call(struct basic_block *s, struct tree_node *tree, - struct insn *call_insn, struct vm_method *method) -{ - struct var_info *offset_reg; - struct var_info *frame_reg; - unsigned long offset_tls; - unsigned long tr_addr; - unsigned long guard; - unsigned long field; - - frame_reg = get_fixed_var(s->b_parent, MACH_REG_xBP); - offset_reg = get_var(s->b_parent, J_REFERENCE); - - offset_tls = get_thread_local_offset(&jni_stack_offset); - select_insn(s, tree, - memdisp_reg_insn(INSN_MOV_THREAD_LOCAL_MEMDISP_REG, - offset_tls, offset_reg)); - - tr_addr = get_thread_local_offset(&jni_stack); - - /* Check for stack overflow */ - guard = (unsigned long) jni_stack_offset_guard; - select_insn(s, tree, membase_reg_insn(INSN_TEST_MEMBASE_REG, - offset_reg, guard, offset_reg)); - - /* Advance jni_stack_offset */ - select_insn(s, tree, - imm_reg_insn(INSN_ADD_IMM_REG, sizeof(struct jni_stack_entry), - offset_reg)); - select_insn(s, tree, - reg_memdisp_insn(INSN_MOV_REG_THREAD_LOCAL_MEMDISP, - offset_reg, offset_tls)); - - tr_addr -= sizeof(struct jni_stack_entry); - - /* Set ->caller_frame */ - field = tr_addr + offsetof(struct jni_stack_entry, caller_frame); - select_insn(s, tree, reg_membase_insn(INSN_MOV_REG_THREAD_LOCAL_MEMBASE, - frame_reg, offset_reg, field)); - - /* Set ->call_site_addr */ - field = tr_addr + offsetof(struct jni_stack_entry, call_site_addr); - select_jni_set_call_site_addr(s, tree, offset_reg, field); - - /* Set ->method */ - field = tr_addr + offsetof(struct jni_stack_entry, method); - select_jni_set_method(s, tree, method, offset_reg, field); - - select_insn(s, tree, call_insn); - - /* Restore jni_stack_ffset (pop) */ - select_insn(s, tree, - imm_reg_insn(INSN_SUB_IMM_REG, sizeof(struct jni_stack_entry), - offset_reg)); - select_insn(s, tree, - reg_memdisp_insn(INSN_MOV_REG_THREAD_LOCAL_MEMDISP, - offset_reg, offset_tls)); -} - -#ifdef CONFIG_X86_32 static void select_set_target(struct basic_block *s, struct tree_node *tree, void *target, @@ -3006,9 +2889,7 @@ static void invoke(struct basic_block *s, struct tree_node *tree, struct compila call_insn = rel_insn(INSN_CALL_REL, (unsigned long) target); - if (vm_method_is_jni(method)) - select_jni_call(s, tree, call_insn, method); - else if (vm_method_is_vm_native(method)) + if (vm_method_is_vm_native(method)) select_vm_native_call(s, tree, call_insn, vm_method_native_ptr(method)); else @@ -3065,10 +2946,7 @@ static void invokevirtual(struct _MBState *state, struct basic_block *s, struct /* invoke method */ call_insn = reg_insn(INSN_CALL_REG, call_target); - if (vm_method_is_jni(method)) - select_jni_call(s, tree, call_insn, method); - else - select_insn(s, tree, call_insn); + select_insn(s, tree, call_insn); nr_stack_args = get_stack_args_count(method); if (nr_stack_args) diff --git a/include/jit/emit-code.h b/include/jit/emit-code.h index d012c36..0b7c898 100644 --- a/include/jit/emit-code.h +++ b/include/jit/emit-code.h @@ -8,6 +8,7 @@ struct buffer; struct insn; struct vm_object; struct vm_jni_env; +struct vm_method; enum emitter_type { NO_OPERANDS = 1, @@ -40,5 +41,6 @@ extern void emit_insn(struct buffer *, struct basic_block *, struct insn *); extern void emit_nop(struct buffer *buf); extern void backpatch_branch_target(struct buffer *buf, struct insn *insn, unsigned long target_offset); +extern void emit_jni_trampoline(struct buffer *, struct vm_method *, void *); #endif /* JATO_EMIT_CODE_H */ diff --git a/include/vm/stack-trace.h b/include/vm/stack-trace.h index dcb68ac..de04ad5 100644 --- a/include/vm/stack-trace.h +++ b/include/vm/stack-trace.h @@ -16,7 +16,7 @@ struct vm_class; struct jni_stack_entry { void *caller_frame; - unsigned long call_site_addr; + unsigned long return_address; /* We don't know the address of JNI callee at compilation time * so code generated for JNI call site stores a pointer to @@ -51,10 +51,10 @@ extern __thread unsigned long jni_stack_offset; extern __thread struct vm_native_stack_entry vm_native_stack[VM_NATIVE_STACK_SIZE]; extern __thread unsigned long vm_native_stack_offset; -int vm_enter_jni(void *caller_frame, unsigned long call_site_addr, - struct vm_method *method); +int vm_enter_jni(void *caller_frame, struct vm_method *method, + unsigned long return_address); int vm_enter_vm_native(void *target, void *stack_ptr); -void vm_leave_jni(void); +unsigned long vm_leave_jni(void); void vm_leave_vm_native(void); static inline int jni_stack_index(void) diff --git a/jit/args.c b/jit/args.c index 3da92a8..46caee0 100644 --- a/jit/args.c +++ b/jit/args.c @@ -33,7 +33,6 @@ #include "vm/method.h" #include "vm/stack.h" -#include "vm/jni.h" #ifdef CONFIG_ARGS_MAP int get_stack_args_count(struct vm_method *method) @@ -70,22 +69,11 @@ insert_arg(struct expression *root, struct expression *_expr; /* Check if we should put @expr in EXPR_ARG_THIS. */ - if (!vm_method_is_static(method)) { - if (vm_method_is_jni(method)) { - if (index == 1) { - _expr = arg_this_expr(expr); - goto from_expr_this; - } - } else - if (index == 0) { - _expr = arg_this_expr(expr); - goto from_expr_this; - } - } - - _expr = arg_expr(expr); + if (!vm_method_is_static(method) && index == 0) + _expr = arg_this_expr(expr); + else + _expr = arg_expr(expr); - from_expr_this: _expr->bytecode_offset = expr->bytecode_offset; set_expr_arg_reg(_expr, method, index); @@ -100,7 +88,6 @@ struct expression *convert_args(struct stack *mimic_stack, struct vm_method *method) { struct expression *args_list = NULL; - unsigned long nr_args_total; unsigned long i; if (nr_args == 0) { @@ -108,15 +95,6 @@ struct expression *convert_args(struct stack *mimic_stack, goto out; } - nr_args_total = nr_args; - - if (vm_method_is_jni(method)) { - if (vm_method_is_static(method)) - --nr_args; - - --nr_args; - } - /* * We scan the args map in reverse order, * since the order of arguments is already reversed. @@ -124,35 +102,7 @@ struct expression *convert_args(struct stack *mimic_stack, for (i = 0; i < nr_args; i++) { struct expression *expr = stack_pop(mimic_stack); args_list = insert_arg(args_list, expr, - method, nr_args_total - i - 1); - } - - if (vm_method_is_jni(method)) { - struct expression *jni_env_expr; - - if (vm_method_is_static(method)) { - struct expression *class_expr; - - class_expr = value_expr(J_REFERENCE, - (unsigned long) method->class->object); - - if (!class_expr) { - expr_put(args_list); - return NULL; - } - - args_list = insert_arg(args_list, class_expr, - method, 1); - } - - jni_env_expr = value_expr(J_REFERENCE, - (unsigned long)vm_jni_get_jni_env()); - if (!jni_env_expr) { - expr_put(args_list); - return NULL; - } - - args_list = insert_arg(args_list, jni_env_expr, method, 0); + method, nr_args - i - 1); } out: diff --git a/jit/trampoline.c b/jit/trampoline.c index 1637a77..009d514 100644 --- a/jit/trampoline.c +++ b/jit/trampoline.c @@ -49,8 +49,6 @@ static void *jit_jni_trampoline(struct compilation_unit *cu) struct vm_method *method; void *ret; - enter_vm_from_jni(); - method = cu->method; class_name = method->class->name; @@ -59,12 +57,21 @@ static void *jit_jni_trampoline(struct compilation_unit *cu) ret = vm_jni_lookup_method(class_name, method_name, method_type); if (ret) { - add_cu_mapping((unsigned long)ret, cu); + struct buffer *buf; + + if (add_cu_mapping((unsigned long)ret, cu)) + return NULL; + + buf = alloc_exec_buffer(); + if (!buf) + return NULL; + + emit_jni_trampoline(buf, method, ret); - cu->native_ptr = ret; + cu->native_ptr = buffer_ptr(buf); cu->is_compiled = true; - return ret; + return cu->native_ptr; } struct string *msg = alloc_str(); @@ -81,12 +88,6 @@ static void *jit_jni_trampoline(struct compilation_unit *cu) signal_new_exception(vm_java_lang_UnsatisfiedLinkError, msg->value); free_str(msg); - /* We must remove the jni_stack_entry from call stack here - * because we're not returning after call site - exception - * will be caught in trampoline code and delivered to handler. - */ - vm_leave_jni(); - return NULL; } diff --git a/regression/jvm/StackTraceTest.java b/regression/jvm/StackTraceTest.java index f50957e..e680335 100644 --- a/regression/jvm/StackTraceTest.java +++ b/regression/jvm/StackTraceTest.java @@ -95,13 +95,9 @@ public class StackTraceTest extends TestCase { } assertNotNull(st); - assertEquals(3, st.length); - - assertStackTraceElement(st[0], -1, null, - "jvm.StackTraceTest", - "nativeMethod", true); + assertEquals(2, st.length); - assertStackTraceElement(st[1], 92, "StackTraceTest.java", + assertStackTraceElement(st[0], 92, "StackTraceTest.java", "jvm.StackTraceTest", "testJNIUnsatisfiedLinkErrorStackTrace", false); diff --git a/test/vm/stack-trace-stub.c b/test/vm/stack-trace-stub.c index f54172a..38e3df7 100644 --- a/test/vm/stack-trace-stub.c +++ b/test/vm/stack-trace-stub.c @@ -28,14 +28,15 @@ void vm_leave_vm_native(void) { } -int vm_enter_jni(void *caller_frame, unsigned long call_site_addr, - struct vm_method *method) +int vm_enter_jni(void *caller_frame, struct vm_method *method, + unsigned long return_address) { return 0; } -void vm_leave_jni(void) +unsigned long vm_leave_jni(void) { + return 0; } void init_stack_trace_elem(struct stack_trace_elem *elem, unsigned long addr, diff --git a/vm/call.c b/vm/call.c index 76fc83e..c286ac8 100644 --- a/vm/call.c +++ b/vm/call.c @@ -34,7 +34,6 @@ #include "vm/call.h" #include "vm/class.h" -#include "vm/jni.h" #include "vm/method.h" #include "vm/object.h" #include "vm/stack-trace.h" @@ -63,16 +62,6 @@ vm_call_method_a(struct vm_method *method, unsigned long *args) target = vm_method_call_ptr(method); - if (vm_method_is_jni(method)) { - if (vm_enter_jni(__builtin_frame_address(0), - (unsigned long) &&call_site, method)) - return -1; - call_site: - native_call(target, args, method->args_count, result); - vm_leave_jni(); - goto out; - } - native_call(target, args, method->args_count, result); out: @@ -82,52 +71,10 @@ vm_call_method_a(struct vm_method *method, unsigned long *args) return result; } - -static unsigned long -vm_call_jni_method_v(struct vm_method *method, va_list args) -{ - unsigned long args_array[method->args_count]; - int i; - - i = 0; - args_array[i++] = (unsigned long)vm_jni_get_jni_env(); - - if (vm_method_is_static(method)) - args_array[i++] = (unsigned long)method->class->object; - - while (i < method->args_count) - args_array[i++] = va_arg(args, long); - - return vm_call_method_a(method, args_array); -} - -static unsigned long -vm_call_jni_method_this_v(struct vm_method *method, struct vm_object *this, - va_list args) -{ - unsigned long args_array[method->args_count]; - int i; - - i = 0; - args_array[i++] = (unsigned long)vm_jni_get_jni_env(); - - assert(!vm_method_is_static(method)); - - args_array[i++] = (unsigned long) this; - - while (i < method->args_count) - args_array[i++] = va_arg(args, long); - - return vm_call_method_a(method, args_array); -} - unsigned long vm_call_method_v(struct vm_method *method, va_list args) { unsigned long args_array[method->args_count]; - if (vm_method_is_jni(method)) - return vm_call_jni_method_v(method, args); - for (int i = 0; i < method->args_count; i++) args_array[i] = va_arg(args, unsigned long); @@ -140,9 +87,6 @@ unsigned long vm_call_method_this_v(struct vm_method *method, { unsigned long args_array[method->args_count]; - if (vm_method_is_jni(method)) - return vm_call_jni_method_this_v(method, this, args); - args_array[0] = (unsigned long) this; for (int i = 1; i < method->args_count; i++) diff --git a/vm/method.c b/vm/method.c index ffdc021..095c1de 100644 --- a/vm/method.c +++ b/vm/method.c @@ -76,13 +76,6 @@ int vm_method_init(struct vm_method *vmm, if (vm_method_is_native(vmm)) { vmm->is_vm_native = vm_lookup_native(vmm->class->name, vmm->name); - - if (vm_method_is_jni(vmm)) { - if (vm_method_is_static(vmm)) - ++vmm->args_count; - - ++vmm->args_count; - } } if (args_map_init(vmm)) { diff --git a/vm/stack-trace.c b/vm/stack-trace.c index 5f4acea..9383333 100644 --- a/vm/stack-trace.c +++ b/vm/stack-trace.c @@ -104,8 +104,8 @@ static inline struct vm_native_stack_entry *new_vm_native_stack_entry(void) return tr; } -int vm_enter_jni(void *caller_frame, unsigned long call_site_addr, - struct vm_method *method) +int vm_enter_jni(void *caller_frame, struct vm_method *method, + unsigned long return_address) { if (jni_stack_is_full()) { struct vm_object *e = vm_alloc_stack_overflow_error(); @@ -119,7 +119,7 @@ int vm_enter_jni(void *caller_frame, unsigned long call_site_addr, struct jni_stack_entry *tr = new_jni_stack_entry(); tr->caller_frame = caller_frame; - tr->call_site_addr = call_site_addr; + tr->return_address = return_address; tr->method = method; return 0; } @@ -142,9 +142,10 @@ int vm_enter_vm_native(void *target, void *stack_ptr) return 0; } -void vm_leave_jni() +unsigned long vm_leave_jni() { jni_stack_offset -= sizeof(struct jni_stack_entry); + return jni_stack[jni_stack_index()].return_address; } void vm_leave_vm_native() @@ -172,7 +173,7 @@ int stack_trace_elem_next(struct stack_trace_elem *elem) &jni_stack[elem->jni_stack_index--]; new_frame = tr->caller_frame; - new_addr = tr->call_site_addr; + new_addr = tr->return_address - 1; goto out; } -- 1.6.0.6 ------------------------------------------------------------------------------ Let Crystal Reports handle the reporting - Free Crystal Reports 2008 30-Day trial. Simplify your report design, integration and deployment - and focus on what you do best, core application coding. Discover what's new with Crystal Reports now. http://p.sf.net/sfu/bobj-july _______________________________________________ Jatovm-devel mailing list Jatovm-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/jatovm-devel