通用Thunk
2010-07-15 20:45:23 来源:WEB开发网原理
原理中最重要的是函数的调用约定(Calling Convention) ,调用者和被调者之间的约定。普通C函数通常使用3种调用约定 : “__cdecl” “__stdcall” “__fastcall” 成员函数通常使用 “__thiscall””__stdcall” “__cdecl”
我们需要着重关注以下3点:
调用者(普通C函数)怎么准备参数和返回地址?
被调用者(成员函数)希望并且要求的参数和返回地址是什么?它如何取得它们?
平衡堆栈是谁的责任?
调用者准备的参数和返回地址总不是被调用者所期待的那样,因为被调用者还需要一个this指针。平衡堆栈的方式也许也会不同。我们的工作就是以被调用者期望的方式,准备好this指针,同时弥补2者在平衡堆栈上的差异。
为了简单起见,我们以 “ void func(int); void C::func(int); ”为例,首先,我们来看看当使用__stdcall 约定的func被调用的时候,会发生什么。
func(1212); 编译器会像这样准备参数和返回地址 :
PUSH 1212 ; 使得堆栈增加4
CALL func; 使得堆栈也增加4(因为返回地址也被压入堆栈)
0x50000:...;被调用者返回这里,我们假设这里的地址是0x50000
调用者希望被调用者使用 RET 4 (使得堆栈减少8:参数1212使用4,返回地址0x50000也使用4)来平衡堆栈,所以在这之后没有多余的机器码。所以,在这之后,堆栈是这个样子:
...
1212
0x50000 <- ESP
然后,我们来看看使用__thiscall 的被调用者所希望的参数和返回地址。一个真正的成员函数被调用时。
C obj;
obj.func(1212);
编译器以这样的方式准备参数:
PUSH 1212;
MOV ECX,obj;
CALL C::func
所以,在这之后,堆栈是这个样子:
…
1212
0x50000 <- ESP
ECX 保存着 this 指针。
这也就是被调用者(void __thiscall C::func(int); ) 需要的形式。
更多精彩
赞助商链接