CallStub相关

调用入口

share/vm/runtime/stubRoutines.hpp

  // Calls to Java SimonNote: 函数指针结合typedef类型定义
typedef void (*CallStub)(
address link,
intptr_t* result,
BasicType result_type,
Method* method,
address entry_point,
intptr_t* parameters,
int size_of_parameters,
TRAPS
);
// Calls to Java SimonNote: 将内存地址 转换成函数指针 CAST_TO_FN_PTR ((CallStub)(castable_address(_call_stub_entry)))
static CallStub call_stub() { return CAST_TO_FN_PTR(CallStub, _call_stub_entry); }

CAST_TO_FN_PTR 宏转换成 ((CallStub)(castable_address(_call_stub_entry)))

share/vm/runtime/javaCalls.cpp中调用call_stub部分

      StubRoutines::call_stub()(
(address)&link,
// (intptr_t*)&(result->_value), // see NOTE above (compiler problem)
result_val_address, // see NOTE above (compiler problem)
result_type,
method(),
entry_point,
args->parameters(),
args->size_of_parameters(),
CHECK
);

注意: call_stub 后面先接了一个()拿到函数指针后又接了一个括号含参数列表

入参

8个入参及含义

编号 参数名称 含义 压栈位置 call entry_point时的位置
1 link 连接器 JavaCallWrapper类型,可以理解成调用上下文 2N(%ebp) 2N(%ebp)
2 result_val_address 函数返回值地址 3N(%ebp) 3N(%ebp)
3 result_type 函数返回类型 4N(%ebp) 4N(%ebp
4 method() JVM内部所表示的JAVA方法对象 5N(%ebp) ebx
5 entry_point JVM调用JAVA方法例程入口。JVM内部的每一段例程是在启动时生成好。要调用JAVA方法,都需要经过本例程。然后才跳转到JAVA方法字节码所对应的机器指令去执行。 6N(%ebp) eax
6 args->parameters() JAVA方法入参集合 7N(%ebp) edx
7 args->size_of_parameters() JAVA方法入参数量 8N(%ebp) ecx
8 CHECK 当前线程对象 9N(%ebp) 9N(%ebp)

CHECK 宏展开 the_thread); if ((((ThreadShadow*)the_thread)->has_pending_exception())) return ; (void)(0

JVM调用java程序main()主函数的路线图:

JVM主函数--> 调用CallStub(_call_stub_entry例程)--> 调用entry_point(entry_point例程)--> 调用Java函数

_call_stub_entry例程生成好了之后指向函数首地址,然后在调用时会将其强转成函数指针,这个函数就是CallStub。转就是CAST_TO_FN_PTR宏干的。

_call_stub_entry例程生成

cpu/x86/vm/stubGenerator_x86_64.cpp

  void generate_initial() {
// Generates all stubs and initializes the entry points // This platform-specific settings are needed by generate_call_stub()
create_control_words(); // entry points that exist in all platforms Note: This is code
// that could be shared among different platforms - however the
// benefit seems to be smaller than the disadvantage of having a
// much more complicated generator structure. See also comment in
// stubRoutines.hpp. StubRoutines::_forward_exception_entry = generate_forward_exception(); StubRoutines::_call_stub_entry =
generate_call_stub (StubRoutines::_call_stub_return_address);
....

generate_call_stub 所做的事情就是将CallStub的函数体直接以机器码的形式写入内存区域。很猛!

大概逻辑:

  1. c++层面的参数入栈
  2. 计算实际被调用的java方法的参数大小(这个是在编译期算出来的)算出需要的栈大小
  3. 计算 rdi rsi rbx mxcsr 四个寄存器所占用的栈空间大小
  4. 将上面计算出来的栈大小(在ecx寄存器中)拿出来完成栈空间分配,sub %ecx %esp。至此,JVM完成了动态栈分配。
  5. 调用者保存,因为是CallStub调用了java方法,那么java方法的调用者就是CallStub。现在需要保存调用者自己的寄存器数据。主要包括edi,esi,edx。在JVM中,esi存放java指令偏移地址,ebx存放java指令基地址。调用者保存实际是将刚才几个寄存器的值在栈中暂存。
  6. 参数压栈。是将java方法的参数入栈。采用循环迭代参数的方式。汇编层面用了跳转指令,有用到test je jne dec inc mov指令。java函数入参数量在ecx物理寄存器中。edx中存放的是parameters首地址。压完之后 栈帧中数据大概是 C++的CallStub的8个入参,eip ebp加上步骤3的4个寄存器共6个的暂存值,接着是java方法的入参。
  7. 调用entry_point例程。entry_point也是函数指针。在调用entry_point例程之前,会生成一些机器码将CallStub一些入参放入寄存器,有:将method首地址放到ebx;将entry_point放到eax,将当前栈顶esp放到esi;放的方式都是基于一开始压栈的地址处理的。比如entry_point,mov 0x18(%ebp), %eax 。然后调用entry_point时,直接call ×%eax即可。为什么要将这些放入寄存器,因为调用entry_point时用的call指令,call指令会“切换新的栈帧”(是这么个意思),所以之前的这些对象就不好基于栈再寻址了,就将其先放到寄存器里。另外没放的4个参数在entry_point例程中用不到。
  8. 获取entry_point的返回值。调用完之后 会将栈上的被调用者的返回值与返回类型mov到edi和esi两个寄存器中。调用放要用时就到这个寄存器中获取。

最新文章

  1. redis之理解
  2. Glide加载图片到自定义的圆形ImageView中不显示
  3. PLS-00306错误
  4. 【Android】应用程序启动过程源码分析
  5. 转 -android:程序无响应,你该如何定位问题?
  6. Ubuntu打开终端和设置root密码(转载)
  7. C++学习2
  8. 表格对象QTableWidget相关常见方法
  9. c语言中结构体的定义、初始化及内存分配
  10. Core OS 层
  11. (转)PHP文件没有结尾的?>有什么好处?
  12. birt报表中使用多个数据集。
  13. B. 沙漠之旅(分组背包)
  14. Storm容错和高可用
  15. memcached实战系列(七)理解Memcached的数据过期方式、新建过程、查找过程
  16. IO多路复用三种方式select/poll/epoll
  17. [20181130]control file sequential read.txt
  18. python nmap
  19. Vue下拉刷新组件
  20. 巧用花生壳将局域网内的FTP和www服务器发布到互联网

热门文章

  1. Python的telnetlib模块使用
  2. 【bfs+链式向前星】防御僵尸(defend)计蒜客 - 45288
  3. canvas学习01
  4. Java中static、final和static final(final static)的区别(转)
  5. static关键字和final关键字
  6. 题解 洛谷 P4171 【[JSOI2010]满汉全席】
  7. Redis知识总结
  8. jmeter之断言、数据提取器(正则表达式、jsonpath、beanshell)、聚合报告、参数化
  9. 关于git的一些简单命令
  10. springboot 基于JS-SDK实现微信分享(一)