Skip to main content

java main

· 10 min read

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;
}

相关阅读