C++ 调用约定

调用约定

调用约定3要素:参数传递,堆栈平衡,返回值存储(存储在寄存器,8086存储在EAX/EDX.EAX)。
__cdecl,C语言默认约定,参数从右往左入栈,调用方平衡堆栈(支持变参)。
__stdcall,WINAPI默认约定,参数从右往左入栈,被调用方平衡堆栈。
__fastcall,前N个参数由寄存器传递(Intel 386平台使用ECX和EDX寄存器),其余参数从右往左入栈,被调用方平衡堆栈。
__thiscall,类默认调用约定,对象this指针通过ECX传递,参数从右往左入栈,被调用方平衡堆栈。
__declspec(naked),配合调用约定使用,需要视情况手动添加代码比如存储环境PUSHAD,恢复环境POPAD,平衡堆栈RETN。

naked注意
不能在函数体当前层级使用局部变量(因为局部变量会占用父函数对应位置的地址会修改值),但可以在{}内使用局部变量。
只能通过esp引用参数,因为子函数继承了父函数的esp。
不能用于函数指针,不能用于数据定义。

函数执行过程(进入与退出反序)
1.按调用约定传递参数
2.返回地址入栈
3.存储环境比如PUSHAD,或者存储可能修改的寄存器比如EDX...(EDX.EAX,ECX不存储)
4.申请局部变量空间,DEBGU时初始化为CC
5.执行用户代码
6.释放局部变量空间
7.恢复环境比如POPAD,或者恢复可能修改的寄存器比如EDX...(EDX.EAX,ECX恢复)
8.返回地址出栈存储到EIP

INT __cdecl Add1(IN INT i1, IN INT i2, IN INT i3, IN INT i4)
{
//00FA38BD push 4
//00FA38BF push 3
//00FA38C1 push 2
//00FA38C3 push 1
//00FA38C5 call Add1 (0F76A08h)
//00FA38CA add esp,10h
return i1 + i2 + i3 + i4;
}
INT __stdcall Add2(IN INT i1, IN INT i2, IN INT i3, IN INT i4)
{
//00FA38D0 push 4
//00FA38D2 push 3
//00FA38D4 push 2
//00FA38D6 push 1
//00FA38D8 call Add2 (0F769FEh)
return i1 + i2 + i3 + i4;
}
INT __fastcall Add3(IN INT i1, IN INT i2, IN INT i3, IN INT i4)
{
//00FA38E0 push 4
//00FA38E2 push 3
//00FA38E4 mov edx,2
//00FA38E9 mov ecx,1
//00FA38EE call Add3 (0F76A0Dh)
return i1 + i2 + i3 + i4;
}
class ThisCall
{
public:
INT Add4(IN INT i1, IN INT i2, IN INT i3, IN INT i4)
{
//00FA38F6 push 4
//00FA38F8 push 3
//00FA38FA push 2
//00FA38FC push 1
//00FA38FE lea ecx,[obj]
//00FA3901 call ThisCall::Add4 (0F76A03h)
return i1 + i2 + i3 + i4;
}
protected:
private:
};
//X64不支持
INT __declspec(naked) __cdecl Add5(IN INT i1, IN INT i2, IN INT i3, IN INT i4)
{
//00FA38D0 push 4
//00FA38D2 push 3
//00FA38D4 push 2
//00FA38D6 push 1
__asm
{
//mov eax, dword ptr[esp]; //返回地址
mov eax, dword ptr[esp + 4]; //p1
add eax, dword ptr[esp + 8]; //p2
add eax, dword ptr[esp + 0x0C]; //p3
add eax, dword ptr[esp + 0x10]; //p4
retn;
}
}

猜你喜欢

转载自www.cnblogs.com/dailycode/p/9438977.html