call_stub–JVM Java调用的入口

原创文章,转载请注明: 转载自慢慢的回味

本文链接地址: 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 国际许可协议进行许可。

发表回复