原创文章,转载请注明: 转载自慢慢的回味
本文链接地址: call_stub–JVM Java调用的入口
调用堆栈:
JavaCalls::call_helper() at javaCalls.cpp:393 0x7ffff70f3600 os::os_exception_wrapper() at os_linux.cpp:5,104 0x7ffff7372b40 JavaCalls::call() at javaCalls.cpp:307 0x7ffff70f2fe6 InstanceKlass::call_class_initializer_impl() at instanceKlass.cpp:1,193 0x7ffff7088edf InstanceKlass::call_class_initializer() at instanceKlass.cpp:1,161 0x7ffff7088c6d InstanceKlass::initialize_impl() at instanceKlass.cpp:897 0x7ffff7087a40 InstanceKlass::initialize() at instanceKlass.cpp:557 0x7ffff7085f3e InstanceKlass::initialize_impl() at instanceKlass.cpp:840 0x7ffff70874d7 InstanceKlass::initialize() at instanceKlass.cpp:557 0x7ffff7085f3e initialize_class() at thread.cpp:983 0x7ffff74c5244 Threads::create_vm() at thread.cpp:3,497 0x7ffff74cc7df JNI_CreateJavaVM() at jni.cpp:5,166 0x7ffff7134f13 |
下面通过代码注释的方式来介绍call_helper这个方法
void JavaCalls::call_helper(JavaValue* result, methodHandle* m, JavaCallArguments* args, TRAPS) { //JVM通过OOP-Klass的方式来对Java的类实例-类进行描述。Handle是对OOP和Klass的间接引用。 //Handle类里重载了操作符()和->,通过method()或method->可以获取到methodOop。 methodHandle method = *m; JavaThread* thread = (JavaThread*)THREAD; assert(thread->is_Java_thread(), "must be called by a java thread"); assert(method.not_null(), "must have a method to call"); assert(!SafepointSynchronize::is_at_safepoint(), "call to Java code during VM operation"); assert(!thread->handle_area()->no_handle_mark_active(), "cannot call out to Java here"); CHECK_UNHANDLED_OOPS_ONLY(thread->clear_unhandled_oops();) // Verify the arguments if (CheckJNICalls) { args->verify(method, result->get_type(), thread); } else debug_only(args->verify(method, result->get_type(), thread)); // Ignore call if method is empty if (method->is_empty_method()) { assert(result->get_type() == T_VOID, "an empty method must return a void value"); return; } #ifdef ASSERT { InstanceKlass* holder = method->method_holder(); // A klass might not be initialized since JavaCall's might be used during the executing of // the . For example, a Thread.start might start executing on an object that is // not fully initialized! (bad Java programming style) assert(holder->is_linked(), "rewritting must have taken place"); } #endif assert(!thread->is_Compiler_thread(), "cannot compile from the compiler"); if (CompilationPolicy::must_be_compiled(method)) { CompileBroker::compile_method(method, InvocationEntryBci, CompilationPolicy::policy()->initial_compile_level(), methodHandle(), 0, "must_be_compiled", CHECK); } //这儿获取method的entry_point,entry_point就是为Java方法调用准备栈桢,并把代码调用指针指向method的第一个字节码的内存地址,另一篇文章会详细说明entry_point方法的创建。 //entry_point相当于是method的封装,不同的method类型有不同的entry_point。 // Since the call stub sets up like the interpreter we call the from_interpreted_entry // so we can go compiled via a i2c. Otherwise initial entry method will always // run interpreted. address entry_point = method->from_interpreted_entry(); if (JvmtiExport::can_post_interpreter_events() && thread->is_interp_only_mode()) { entry_point = method->interpreter_entry(); } // Figure out if the result value is an oop or not (Note: This is a different value // than result_type. result_type will be T_INT of oops. (it is about size) BasicType result_type = runtime_type_from(result); bool oop_result_flag = (result->get_type() == T_OBJECT || result->get_type() == T_ARRAY); // NOTE: if we move the computation of the result_val_address inside // the call to call_stub, the optimizer produces wrong code. intptr_t* result_val_address = (intptr_t*)(result->get_value_addr()); // Find receiver Handle receiver = (!method->is_static()) ? args->receiver() : Handle(); // When we reenter Java, we need to reenable the yellow zone which // might already be disabled when we are in VM. if (thread->stack_yellow_zone_disabled()) { thread->reguard_stack(); } // Check that there are shadow pages available before changing thread state // to Java if (!os::stack_shadow_pages_available(THREAD, method)) { // Throw stack overflow exception with preinitialized exception. Exceptions::throw_stack_overflow_exception(THREAD, __FILE__, __LINE__, method); return; } else { // Touch pages checked if the OS needs them to be touched to be mapped. os::bang_stack_shadow_pages(); } // do call { JavaCallWrapper link(method, receiver, result, CHECK); { HandleMark hm(thread); // HandleMark used by HandleMarkCleaner //通过call_stub->entry_point->method的调用链,完成Java方法的调用 StubRoutines::call_stub()( (address)&link,//call_stub调用完后,返回值通过link指针带回来 // (intptr_t*)&(result->_value), // see NOTE above (compiler problem) result_val_address, // see NOTE above (compiler problem)返回值地址 result_type,//返回类型 method(),//Java的方法实例 entry_point,//Wrap在method上,不同方法类型的调用入口 args->parameters(),//method的参数 args->size_of_parameters(),//参数大小 CHECK ); result = link.result(); // circumvent MS C++ 5.0 compiler bug (result is clobbered across call) // Preserve oop return value across possible gc points if (oop_result_flag) { thread->set_vm_result((oop) result->get_jobject()); } } } // Exit JavaCallWrapper (can block - potential return oop must be preserved) // Check if a thread stop or suspend should be executed // The following assert was not realistic. Thread.stop can set that bit at any moment. //assert(!thread->has_special_runtime_exit_condition(), "no async. exceptions should be installed"); // Restore possible oop return if (oop_result_flag) { result->set_jobject((jobject)thread->vm_result()); thread->set_vm_result(NULL); } } |
call_stub方法的创建
// Calls to Java typedef void (*CallStub)( address link, intptr_t* result, BasicType result_type, Method* method, address entry_point, intptr_t* parameters, int size_of_parameters, TRAPS ); static CallStub call_stub() { return CAST_TO_FN_PTR(CallStub, _call_stub_entry); } |
call_stub其实是由一个函数指针_call_stub_entry转换而来的,即_call_stub_entry指针指向了内存中的一个函数,它是怎么形成的呢?
它是在JVM启动的时候通过如下生成的。
StubGenerator::generate_call_stub() at stubGenerator_x86_64.cpp:221 0x7ffff745af7e StubGenerator::generate_initial() at stubGenerator_x86_64.cpp:3,809 0x7ffff74719e2 StubGenerator::StubGenerator() at stubGenerator_x86_64.cpp:3,912 0x7ffff7471e97 StubGenerator_generate() at stubGenerator_x86_64.cpp:3,918 0x7ffff745ad3b StubRoutines::initialize1() at stubRoutines.cpp:163 0x7ffff7471f87 stubRoutines_init1() at stubRoutines.cpp:297 0x7ffff747295b init_globals() at init.cpp:101 0x7ffff7080d06 Threads::create_vm() at thread.cpp:3,424 0x7ffff74cc509 JNI_CreateJavaVM() at jni.cpp:5,166 0x7ffff7134f13 |
call_stub代码生成
同上,通过代码注释进行说明。
StubRoutines::_call_stub_entry = generate_call_stub(StubRoutines::_call_stub_return_address); //call_stub方法的生成逻辑是直接向内存空间写入汇编代码 address generate_call_stub(address& return_address) { assert((int)frame::entry_frame_after_call_words == -(int)rsp_after_call_off + 1 && (int)frame::entry_frame_call_wrapper_offset == (int)call_wrapper_off, "adjust this code"); StubCodeMark mark(this, "StubRoutines", "call_stub"); address start = __ pc();//获取当前代码段地址,从此地址开始写入stub方法 // Call stubs are used to call Java from C // 以Linux的call_stub桢为例: // Linux Arguments: // c_rarg0: call wrapper address address // c_rarg1: result address // c_rarg2: result type BasicType // c_rarg3: method Method* // c_rarg4: (interpreter) entry point address // c_rarg5: parameters intptr_t* // 16(rbp): parameter size (in words) int // 24(rbp): thread Thread* // // [ return_from_Java ] <--- rsp | // [ argument word n ] | // ... | // -12 [ argument word 1 ] | // -11 [ saved r15 ] <--- rsp_after_call | // -10 [ saved r14 ] | // -9 [ saved r13 ] | // -8 [ saved r12 ] | // -7 [ saved rbx ] | // -6 [ call wrapper ] |call_stub栈桢 // -5 [ result ] | // -4 [ result type ] | // -3 [ method ] | // -2 [ entry point ] | // -1 [ parameters ] | // 0 [ saved rbp ] <--- rbp | // 1 [ return address ] | // 2 [ parameter size ] | // 3 [ thread ] | // same as in generate_catch_exception()! const Address rsp_after_call(rbp, rsp_after_call_off * wordSize); // 设置相应参数的地址,注意堆栈是向低地址方向增长的,地址存在地址低的一端。 const Address call_wrapper (rbp, call_wrapper_off * wordSize); const Address result (rbp, result_off * wordSize); const Address result_type (rbp, result_type_off * wordSize); const Address method (rbp, method_off * wordSize); const Address entry_point (rbp, entry_point_off * wordSize); const Address parameters (rbp, parameters_off * wordSize); const Address parameter_size(rbp, parameter_size_off * wordSize); // same as in generate_catch_exception()! const Address thread (rbp, thread_off * wordSize); const Address r15_save(rbp, r15_off * wordSize); const Address r14_save(rbp, r14_off * wordSize); const Address r13_save(rbp, r13_off * wordSize); const Address r12_save(rbp, r12_off * wordSize); const Address rbx_save(rbp, rbx_off * wordSize); // stub code __ enter();// 函数的实现是:push(rbp); mov(rbp, rsp); 即保存rbp的值到堆栈供函数返回使用,把rsp的值保存到rbp,即栈顶地址成为新的基址。 __ subptr(rsp, -rsp_after_call_off * wordSize);//堆栈增长rsp_after_call_off个字空间,以供下面使用 // save register parameters 把参数压栈到上面设定的地址 #ifndef _WIN64 __ movptr(parameters, c_rarg5); // parameters __ movptr(entry_point, c_rarg4); // entry_point #endif __ movptr(method, c_rarg3); // method __ movl(result_type, c_rarg2); // result type __ movptr(result, c_rarg1); // result __ movptr(call_wrapper, c_rarg0); // call wrapper // save regs belonging to calling function保存寄存器里面的值,函数调用完后需要恢复,别人的数据不能动。 __ movptr(rbx_save, rbx); __ movptr(r12_save, r12); __ movptr(r13_save, r13); __ movptr(r14_save, r14); __ movptr(r15_save, r15); #ifdef _WIN64 for (int i = 6; i <= 15; i++) { __ movdqu(xmm_save(i), as_XMMRegister(i)); } const Address rdi_save(rbp, rdi_off * wordSize); const Address rsi_save(rbp, rsi_off * wordSize); __ movptr(rsi_save, rsi); __ movptr(rdi_save, rdi); #else const Address mxcsr_save(rbp, mxcsr_off * wordSize); { Label skip_ldmx; __ stmxcsr(mxcsr_save); __ movl(rax, mxcsr_save); __ andl(rax, MXCSR_MASK); // Only check control and mask bits ExternalAddress mxcsr_std(StubRoutines::addr_mxcsr_std()); __ cmp32(rax, mxcsr_std); __ jcc(Assembler::equal, skip_ldmx); __ ldmxcsr(mxcsr_std); __ bind(skip_ldmx); } #endif // Load up thread register __ movptr(r15_thread, thread); __ reinit_heapbase(); #ifdef ASSERT // make sure we have no pending exceptions { Label L; __ cmpptr(Address(r15_thread, Thread::pending_exception_offset()), (int32_t)NULL_WORD); __ jcc(Assembler::equal, L); __ stop("StubRoutines::call_stub: entered with pending exception"); __ bind(L); } #endif // pass parameters if any把函数的参数依次传递到当前堆栈上 BLOCK_COMMENT("pass parameters if any"); Label parameters_done;//把参数大小复制到c_rarg3,如果c_rarg3为0则退出参数传递 __ movl(c_rarg3, parameter_size); __ testl(c_rarg3, c_rarg3); __ jcc(Assembler::zero, parameters_done); Label loop; __ movptr(c_rarg2, parameters); // parameter pointer __ movl(c_rarg1, c_rarg3); // parameter counter is in c_rarg1把参数大小复制到c_rarg1 __ BIND(loop);//循环把参数先传递到rax,然后push到堆栈 __ movptr(rax, Address(c_rarg2, 0));// get parameter __ addptr(c_rarg2, wordSize); // advance to next parameter __ decrementl(c_rarg1); // decrement counter __ push(rax); // pass parameter __ jcc(Assembler::notZero, loop); // call Java function __ BIND(parameters_done); __ movptr(rbx, method); // get Method*保存method到rbx,entry_point里面会从rbx中取 __ movptr(c_rarg1, entry_point); // get entry_point保存entry_point的地址到c_rarg1供调用 __ mov(r13, rsp); // set sender sp保存当前rsp到r13,entry_point里面会用到,rsp即为最后一个参数的地址 BLOCK_COMMENT("call Java function"); __ call(c_rarg1); BLOCK_COMMENT("call_stub_return_address:"); return_address = __ pc();//返回值地址,返回值的下一个地址即为新栈桢entry_point的开始 // store result depending on type (everything that is not // T_OBJECT, T_LONG, T_FLOAT or T_DOUBLE is treated as T_INT) __ movptr(c_rarg0, result); Label is_long, is_float, is_double, exit; __ movl(c_rarg1, result_type); __ cmpl(c_rarg1, T_OBJECT); __ jcc(Assembler::equal, is_long); __ cmpl(c_rarg1, T_LONG); __ jcc(Assembler::equal, is_long); __ cmpl(c_rarg1, T_FLOAT); __ jcc(Assembler::equal, is_float); __ cmpl(c_rarg1, T_DOUBLE); __ jcc(Assembler::equal, is_double); // handle T_INT case __ movl(Address(c_rarg0, 0), rax); __ BIND(exit); // pop parameters __ lea(rsp, rsp_after_call); #ifdef ASSERT // verify that threads correspond { Label L, S; __ cmpptr(r15_thread, thread); __ jcc(Assembler::notEqual, S); __ get_thread(rbx); __ cmpptr(r15_thread, rbx); __ jcc(Assembler::equal, L); __ bind(S); __ jcc(Assembler::equal, L); __ stop("StubRoutines::call_stub: threads must correspond"); __ bind(L); } #endif // restore regs belonging to calling function #ifdef _WIN64 for (int i = 15; i >= 6; i--) { __ movdqu(as_XMMRegister(i), xmm_save(i)); } #endif __ movptr(r15, r15_save); __ movptr(r14, r14_save); __ movptr(r13, r13_save); __ movptr(r12, r12_save); __ movptr(rbx, rbx_save); #ifdef _WIN64 __ movptr(rdi, rdi_save); __ movptr(rsi, rsi_save); #else __ ldmxcsr(mxcsr_save); #endif // restore rsp __ addptr(rsp, -rsp_after_call_off * wordSize); // return __ pop(rbp); __ ret(0); // handle return types different from T_INT __ BIND(is_long); __ movq(Address(c_rarg0, 0), rax); __ jmp(exit); __ BIND(is_float); __ movflt(Address(c_rarg0, 0), xmm0); __ jmp(exit); __ BIND(is_double); __ movdbl(Address(c_rarg0, 0), xmm0); __ jmp(exit); return start; } |
下一篇文章说说entry_point的创建。本作品采用知识共享署名 4.0 国际许可协议进行许可。