文章目录
虚拟机
要让CPU执行一段代码,只需将CS:IP段寄存器指向代码入口处即可.
CS寄存器保存段地址,IP保存偏移地址
常见汇编
数据传输
- mov1
//将自然数1放入eax寄存器
mov1 1 %eax
- pop
//将栈顶数据弹出至eax寄存器
pop %eax
算术运算指令
- add
// 将自然数3与eax寄存器中的数累加,将结果存储进eax
add 3 , %eax
- inc
// 将ebx寄存器中数增1
inc %ebx
逻辑运算指令
- sh1
//将eax中的数左移一个二进位
sh1 %eax,1
- and
//对a1寄存器中数和操作数进行与操作
and a1,00111011B
串指令
程序转移指令
JVM指令
数据交换
函数调用
运算指令
控制转移
对象创建&类型转换
方法调用
汇编
保存栈基并分配新栈
// 保存调用者(main的调用者是OS)的栈基地址
push1 %ebp
//将调用者的栈基地址指向其栈顶
mov1 %esp,%ebp
//分配栈空间,sub1表示减(因为栈是从大向小)
sub1 $32,%esp
运行结果(64位)如下图
后面可以发现其实栈分配的有多的,但是为了对齐(无论32位还是64位,都需要是64字节整数倍)
初始化数据
//将数字5和3分别放入栈中,20和24表示偏移量.另外28(%esp)是预留给方法返回值
mov1 $5,20(%esp)
mov1 $3,24(%esp)
压栈
// 借助%eax将两个数(被调用方法的入参)放入栈顶
mov1 24(%esp),%eax
mov1 %eax,4(%esp)
mov1 20(%esp),%eax
mov1 %eax,(%esp)
效果如下图
调用
calladd
//将add函数返回的结果(默认在%eax中)放入自己的返回值存放处(28(%esp))
mov1 %eax,28(%esp)
返回
mov1 $0,%eax
leave
ret
eip & ebp
被调用的add代码如下
add:
push1 %ebp
mov1 %esp,%ebp
sub1 $16,%esp
// 从main的栈顶获取入参
mov1 12(%ebp),%eax
mov1 8(%ebp),%edx
// 执行运算
add1 %edx,%eax
mov1 %eax,-4(%ebp)
//返回
mov1 -4(%ebp),%eax
leave
ret
运行时栈如图所示
eip用来存放调用后返回时调用函数应该执行的指令的地址(本例中是calladd下面一条指令的地址),这个是由calladd这条指令由物理机器自动完成的,这样就知道add函数调用完之后main函数从哪儿执行
ebp是由被调用函数的第一行推入的
leave其实它相当于以下指令
mov %ebp,%esp
pop %eip
C
对于linux平台而言,调用者函数向被调用者函数传递参数时,采用逆向顺序压栈,及最后一个参数第一个压栈,而第一个参数最后一个压栈
函数指针
两种定义方式
-
直接声明
return_type (*func_pointer)(date_type1 arg1,date_type2 arg2...data_typen argn);
-
通过类型声明
typedef (*func_pointer_type)(date_type1 arg1,data_type2,arg2...date_typen argn);
采用下面一种方式只是声明了函数指针,如果想初始化(实例化)一个函数指针还需要执行下列语句
func_pointer_type func_pointer
两种调用方式
- (*funcPointer)(参数列表)
- funcPointer(参数列表)
采用第二种方式就和普通的函数调用一致,所有写成第一种可以更明确
CallStub
CAST_TO_FN_PTR
按照globalDefinitions.hpp中的定义如下,这个看上去是按照cpp的语法,和书中的c有所区别
// ANSI C++ does not allow casting from one pointer type to a function pointer
// directly without at best a warning. This macro accomplishes it silently
// In every case that is present at this point the value be cast is a pointer
// to a C linkage function. In some case the type used for the cast reflects
// that linkage and a picky compiler would not complain. In other cases because
// there is no convenient place to place a typedef with extern C linkage (i.e
// a platform dependent header file) it doesn't. At this point no compiler seems
// picky enough to catch these instances (which are few). It is possible that
// using templates could fix these for all cases. This use of templates is likely
// so far from the middle of the road that it is likely to be problematic in
// many C++ compilers.
//
#define CAST_TO_FN_PTR(func_type, value) (reinterpret_cast<func_type>(value))
#define CAST_FROM_FN_PTR(new_type, func_ptr) ((new_type)((address_word)(func_ptr)))
上面的reinterpret_cast是C++的关键字,可以看做C++的强制转换
call_stub
按照stubRoutines.hpp中的定义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调用的地方是javaCalls.cpp的,这个方法很长,有各种if宏判断,这里通过调用call_stub()得到一个需要八个参数的函数并调用
其中八个参数的含义如下:
- link
连接器 - result
函数返回值地址 - result_type
函数返回类型 - mothod
jvm内部所表示的Java方法对象 - entry_point
java方法例程入口 - parameters
java方法的入参集合 - size_of_parameters
入参数量 - TRAPS
当前线程对象
// do call
{ JavaCallWrapper link(method, receiver, result, CHECK);
{ HandleMark hm(thread); // HandleMark used by HandleMarkCleaner
// 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());
intptr_t* parameter_address = args->parameters();
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,
parameter_address,
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)
link
仅仅从CallStub上来看link仅仅是一个address,但是是什么address,根据上面代码也可以看出来是JavaCallWrapper,这个类定义在javaCalls.hpp中,基本上算是一个实体类,具体代码如下:
// A JavaCallWrapper is constructed before each JavaCall and destructed after the call.
// Its purpose is to allocate/deallocate a new handle block and to save/restore the last
// Java fp/sp. A pointer to the JavaCallWrapper is stored on the stack.
class JavaCallWrapper: StackObj {
friend class VMStructs;
private:
JavaThread* _thread; // the thread to which this call belongs
JNIHandleBlock* _handles; // the saved handle block
Method* _callee_method;//to be able to collect arguments if entry frame is top frame
oop _receiver; // the receiver of the call (if a non-static call)
JavaFrameAnchor _anchor; // last thread anchor state that we must restore
JavaValue* _result; // result value
public:
// Construction/destruction
JavaCallWrapper(const methodHandle& callee_method, Handle receiver, JavaValue* result, TRAPS);
~JavaCallWrapper();
// Accessors
JavaThread* thread() const { return _thread; }
JNIHandleBlock* handles() const { return _handles; }
JavaFrameAnchor* anchor(void) { return &_anchor; }
JavaValue* result() const { return _result; }
// GC support
Method* callee_method() { return _callee_method; }
oop receiver() { return _receiver; }
void oops_do(OopClosure* f);
bool is_first_frame() const { return _anchor.last_Java_sp() == NULL; }
};