Skip to content

java main

Posted on:August 24, 2023 at 01:39 PM

main 函数介绍

java的main函数在入口函数 一般都是这个签名

public static void main(String[] argv){

}

那么这个main函数是怎么加载的呢?

调用时机:

jni_invoke_static

(gdb) p method._value->print()
{method}
 - this oop:          0x00007fffb44112d8
 - method holder:     'Hello'
 - constants:         0x00007fffb4411030 constant pool [34] {0x00007fffb4411030} for 'Hello' cache=0x00007fffb44113e0
 - access:            0x9  public static 
 - name:              'main'
 - signature:         '([Ljava/lang/String;)V'
 - max stack:         3
 - max locals:        1
 - size of params:    1
 - method size:       13
 - vtable index:      -2
 - i2i entry:         0x00007fffe100dc00    ///////////  entity_point
 - adapters:          AHE@0x00007ffff01015d0: 0xb i2c: 0x00007fffe1114d60 c2i: 0x00007fffe1114e1a c2iUV: 0x00007fffe1114de4 c2iNCI: 0x00007fffe1114e57
 - compiled entry     0x00007fffe1114e1a
 - code size:         13
 - code start:        0x00007fffb44112c0     // 这里是bytecode 的起点
 - code end (excl):   0x00007fffb44112cd    // 这是bytecode的终点
 - checked ex length: 0
 - linenumber start:  0x00007fffb44112cd
 - localvar length:   0
$7 = void
(gdb) info registers 
rax            0x7ffff59fe940      140737314285888
rbx            0x7fffe1000c9e      140736968264862
rcx            0x7fffb44112d8      140736217551576
rdx            0xa                 10
rsi            0x7ffff59febf8      140737314286584
rdi            0x7ffff59fe940      140737314285888
rbp            0x7ffff59fe870      0x7ffff59fe870
rsp            0x7ffff59fe810      0x7ffff59fe810
r8             0x7fffe100dc00      140736968317952                    // 这里就是入口点
r9             0x7ffff59feaf0      140737314286320
r10            0x7ffff053ae20      140737225403936
r11            0x7ffff0000090      140737219920016
r12            0x1                 1
r13            0x0                 0
r14            0x7ffff7c94850      140737350551632
r15            0x7fffffffa800      140737488332800
rip            0x7fffe1000ca6      0x7fffe1000ca6
eflags         0x202               [ IF ]
cs             0x33                51
ss             0x2b                43
ds             0x0                 0
es             0x0                 0
fs             0x0                 0
gs             0x0                 0
$2 = void
(gdb) where
#0  JavaCalls::call_helper (result=0x7ffff7bfec10, method=..., args=0x7ffff7bfeb30, __the_thread__=0x7ffff00295a0) at /home/ubuntu/jdk/src/hotspot/share/runtime/javaCalls.cpp:333
#1  0x00007ffff6799785 in jni_invoke_static (result=result@entry=0x7ffff7bfec10, method_id=method_id@entry=0x7ffff02c84d0, args=args@entry=0x7ffff7bfec80, __the_thread__=__the_thread__@entry=0x7ffff00295a0, env=0x7ffff00298d0, 
    call_type=JNI_STATIC, receiver=0x0) at /home/ubuntu/jdk/src/hotspot/share/prims/jni.cpp:889
#2  0x00007ffff679cd19 in jni_CallStaticVoidMethod (env=0x7ffff00298d0, cls=<optimized out>, methodID=0x7ffff02c84d0) at /home/ubuntu/jdk/src/hotspot/share/prims/jni.cpp:1713
#3  0x00007ffff7fadcb5 in JavaMain (_args=<optimized out>) at /home/ubuntu/jdk/src/java.base/share/native/libjli/java.c:547
#4  0x00007ffff7fb0f4d in ThreadJavaMain (args=<optimized out>) at /home/ubuntu/jdk/src/java.base/unix/native/libjli/java_md.c:651
#5  0x00007ffff7c94b43 in start_thread (arg=<optimized out>) at ./nptl/pthread_create.c:442
#6  0x00007ffff7d26a00 in clone3 () at ../sysdeps/unix/sysv/linux/x86_64/clone3.S:81
(gdb

c++ 调用java static method方法

调用generate_call_stub ,这是入口点,这个会调用method的entry_point

address StubGenerator::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();

  // 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();
  __ subptr(rsp, -rsp_after_call_off * wordSize);

  // 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
  int last_reg = 15;
  if (UseAVX > 2) {
    last_reg = 31;
  }
  if (VM_Version::supports_evex()) {
    for (int i = xmm_save_first; i <= last_reg; i++) {
      __ vextractf32x4(xmm_save(i), as_XMMRegister(i), 0);
    }
  } else {
    for (int i = xmm_save_first; i <= last_reg; 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, 0xFFC0); // Mask out any pending exceptions (only check control and mask bits)
    ExternalAddress mxcsr_std(StubRoutines::x86::addr_mxcsr_std());
    __ cmp32(rax, mxcsr_std, rscratch1);
    __ jcc(Assembler::equal, skip_ldmx);
    __ ldmxcsr(mxcsr_std, rscratch1);
    __ 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()), 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;
  __ 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
  __ BIND(loop);
  __ 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*
  __ movptr(c_rarg1, entry_point);    // get entry_point
  __ mov(r13, rsp);                   // set sender sp
  BLOCK_COMMENT("call Java function");
  __ call(c_rarg1);

  BLOCK_COMMENT("call_stub_return_address:");
  return_address = __ pc();

  // 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 L1, L2, L3;
    __ cmpptr(r15_thread, thread);
    __ jcc(Assembler::equal, L1);
    __ stop("StubRoutines::call_stub: r15_thread is corrupted");
    __ bind(L1);
    __ get_thread(rbx);
    __ cmpptr(r15_thread, thread);
    __ jcc(Assembler::equal, L2);
    __ stop("StubRoutines::call_stub: r15_thread is modified by call");
    __ bind(L2);
    __ cmpptr(r15_thread, rbx);
    __ jcc(Assembler::equal, L3);
    __ stop("StubRoutines::call_stub: threads must correspond");
    __ bind(L3);
  }
#endif

  __ pop_cont_fastpath();

  // restore regs belonging to calling function
#ifdef _WIN64
  // emit the restores for xmm regs
  if (VM_Version::supports_evex()) {
    for (int i = xmm_save_first; i <= last_reg; i++) {
      __ vinsertf32x4(as_XMMRegister(i), as_XMMRegister(i), xmm_save(i), 0);
    }
  } else {
    for (int i = xmm_save_first; i <= last_reg; 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
  __ vzeroupper();
  __ 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;
}

如何列出汇编代码

// 列出从0x7fffe1000ca6 开始的100 个汇编指令
x/100i   0x7fffe1000ca6

如何用gdb断点地址

(gdb) b *0x7fffe1000ca6

方法入口点

JavaCalls::call_helper
----->  address entry_point = method->from_interpreted_entry();
---------> Atomic::load_acquire(&_from_interpreted_entry)

执行方法的时候会调用 _from_interpreted_entry生成对应的栈以及上下文,其中寄存器r13会指向下一个bytecode , 然后通过r13读取下一个bytecode的例程,并执行对应例程

那么_from_interpreted_entry 是从哪里可以设置的? 在link_method会设置

void Method::link_method(const methodHandle& h_method, TRAPS) {
...
  address entry = Interpreter::entry_for_method(h_method);
  set_interpreter_entry(entry);
...
}

这里的Interpreter::entry_for_method(h_method)是下面这个数组:

AbstractInterpreter::_entry_table  

那么_entry_table是在哪里设置呢?

在下面

void TemplateInterpreterGenerator::generate_all(){

  #define method_entry(kind)                                              \
  { CodeletMark cm(_masm, "method entry point (kind = " #kind ")"); \
    Interpreter::_entry_table[Interpreter::kind] = generate_method_entry(Interpreter::kind); \
  }

  // all non-native method kinds
  method_entry(zerolocals)  // 就是这里会设置AbstractInterpreter::_entry_table[Interpreter::zerolocals]  = generate_method_entry(Interpreter::zerolocals)
}

这里生成的例程就是包括方法帧

address TemplateInterpreterGenerator::generate_normal_entry(bool synchronized) {
  // determine code generation flags
  bool inc_counter  = UseCompiler || CountCompiledCalls || LogTouchedMethods;

  // ebx: Method*
  // rbcp: sender sp
  address entry_point = __ pc();

  const Address constMethod(rbx, Method::const_offset());
  const Address access_flags(rbx, Method::access_flags_offset());
  const Address size_of_parameters(rdx,
                                   ConstMethod::size_of_parameters_offset());
  const Address size_of_locals(rdx, ConstMethod::size_of_locals_offset());


  // get parameter size (always needed)
  __ movptr(rdx, constMethod);
  __ load_unsigned_short(rcx, size_of_parameters);

  // rbx: Method*
  // rcx: size of parameters
  // rbcp: sender_sp (could differ from sp+wordSize if we were called via c2i )

  __ load_unsigned_short(rdx, size_of_locals); // get size of locals in words
  __ subl(rdx, rcx); // rdx = no. of additional locals

  // YYY
//   __ incrementl(rdx);
//   __ andl(rdx, -2);

  // see if we've got enough room on the stack for locals plus overhead.
  generate_stack_overflow_check();

  // get return address
  __ pop(rax);

  // compute beginning of parameters
  __ lea(rlocals, Address(rsp, rcx, Interpreter::stackElementScale(), -wordSize));

  // rdx - # of additional locals
  // allocate space for locals
  // explicitly initialize locals
  {
    Label exit, loop;
    __ testl(rdx, rdx);
    __ jcc(Assembler::lessEqual, exit); // do nothing if rdx <= 0
    __ bind(loop);
    __ push((int) NULL_WORD); // initialize local variables
    __ decrementl(rdx); // until everything initialized
    __ jcc(Assembler::greater, loop);
    __ bind(exit);
  }

  // initialize fixed part of activation frame
  generate_fixed_frame(false);

  // make sure method is not native & not abstract
#ifdef ASSERT
  __ movl(rax, access_flags);
  {
    Label L;
    __ testl(rax, JVM_ACC_NATIVE);
    __ jcc(Assembler::zero, L);
    __ stop("tried to execute native method as non-native");
    __ bind(L);
  }
  {
    Label L;
    __ testl(rax, JVM_ACC_ABSTRACT);
    __ jcc(Assembler::zero, L);
    __ stop("tried to execute abstract method in interpreter");
    __ bind(L);
  }
#endif

  // Since at this point in the method invocation the exception
  // handler would try to exit the monitor of synchronized methods
  // which hasn't been entered yet, we set the thread local variable
  // _do_not_unlock_if_synchronized to true. The remove_activation
  // will check this flag.

  const Register thread = NOT_LP64(rax) LP64_ONLY(r15_thread);
  NOT_LP64(__ get_thread(thread));
  const Address do_not_unlock_if_synchronized(thread,
        in_bytes(JavaThread::do_not_unlock_if_synchronized_offset()));
  __ movbool(do_not_unlock_if_synchronized, true);

  __ profile_parameters_type(rax, rcx, rdx);
  // increment invocation count & check for overflow
  Label invocation_counter_overflow;
  if (inc_counter) {
    generate_counter_incr(&invocation_counter_overflow);
  }

  Label continue_after_compile;
  __ bind(continue_after_compile);

  // check for synchronized interpreted methods
  bang_stack_shadow_pages(false);

  // reset the _do_not_unlock_if_synchronized flag
  NOT_LP64(__ get_thread(thread));
  __ movbool(do_not_unlock_if_synchronized, false);

  // check for synchronized methods
  // Must happen AFTER invocation_counter check and stack overflow check,
  // so method is not locked if overflows.
  if (synchronized) {
    // Allocate monitor and lock method
    lock_method();
  } else {
    // no synchronization necessary
#ifdef ASSERT
    {
      Label L;
      __ movl(rax, access_flags);
      __ testl(rax, JVM_ACC_SYNCHRONIZED);
      __ jcc(Assembler::zero, L);
      __ stop("method needs synchronization");
      __ bind(L);
    }
#endif
  }

  // start execution
#ifdef ASSERT
  {
    Label L;
     const Address monitor_block_top (rbp,
                 frame::interpreter_frame_monitor_block_top_offset * wordSize);
    __ movptr(rax, monitor_block_top);
    __ cmpptr(rax, rsp);
    __ jcc(Assembler::equal, L);
    __ stop("broken stack frame setup in interpreter");
    __ bind(L);
  }
#endif

  // jvmti support
  __ notify_method_entry();

  __ dispatch_next(vtos);     ////////  生成方法帧和上下文执行 , 执行下一个bytecode

  // invocation counter overflow
  if (inc_counter) {
    // Handle overflow of counter and compile method
    __ bind(invocation_counter_overflow);
    generate_counter_overflow(continue_after_compile);
  }

  return entry_point;
}

相关阅读