您的位置:首页 > 编程语言 > C语言/C++

c++函数调用约定学习(二)

2015-02-09 11:18 169 查看
***********************************************************
首先,比较C++ 中的三种函数调用方式。
测试代码:
int _stdcall Add1(int x1, int x2, int x3)
{
return x1 + x2 + x3;
}

int __cdecl Add2(int x1, int x2, int x3)
{
return x1 + x2 + x3;
}

int __fastcall Add3(int x1, int x2, int x3)
{
return x1 + x2 + x3;
}

void FTest2()
{
int x[] = {1, 2, 3};
int ret;

ret = Add1(x[0], x[1], x[2]);
ret = Add2(x[0], x[1], x[2]);
ret = Add3(x[0], x[1], x[2]);
}

先看看在调用这些函数时调用者作些什么工作
__cdecl

_stdcall
__fastcall

ret = Add2(x[0], x[1], x[2]);

ret = Add1(x[0], x[1], x[2]);
ret = Add3(x[0], x[1], x[2]);

0040179F mov eax,dword ptr [ebp-4]
004017A2 push eax
004017A3 mov ecx,dword ptr [ebp-8]
004017A6 push ecx
004017A7 mov edx,dword ptr [x]
004017AA push edx
004017AB call Add2 (401750h)
004017B0 add esp,0Ch
004017B3 mov dword ptr [ret],eax

0040178B mov eax,dword ptr [ebp-4]
0040178E push eax
0040178F mov ecx,dword ptr [ebp-8]
00401792 push ecx
00401793 mov edx,dword ptr [x]
00401796 push edx
00401797 call Add1 (401760h)


0040179C mov dword ptr [ret],eax

004017B6 mov eax,dword ptr [ebp-4]
004017B9 push eax
004017BA mov edx,dword ptr [ebp-8]

004017BD mov ecx,dword ptr [x]

004017C0 call Add3 (401730h)


004017C5 mov dword ptr [ret],eax

从上面可以看出调用时区别:
__cdecl 调用结束后由调用者将参数出栈
_stdcall 调用结束后不需要调用者将参数出栈
__fastcall 调用结束后不需要调用者将参数出栈,并且调用前最左侧两个参数直接存入寄存器EDX 和ECX 中

相同之处是三者均由右向左压栈


再看看在这些函数自己作些什么工作
__cdecl

_stdcall
__fastcall

int __cdecl Add2(int x1, int x2, int x3)
{
00401750 push ebp
00401751 mov ebp,esp



return x1 + x2 + x3;
00401753 mov eax,dword ptr [x1]
00401756 add eax,dword ptr [x2]
00401759 add eax,dword ptr [x3]
}

0040175C pop ebp
0040175D ret

int _stdcall Add1(int x1, int x2, int x3)
{
00401760 push ebp
00401761 mov ebp,esp



return x1 + x2 + x3;
00401763 mov eax,dword ptr [x1]
00401766 add eax,dword ptr [x2]
00401769 add eax,dword ptr [x3]
}

0040176C pop ebp
0040176D ret 0Ch

int __fastcall Add3(int x1, int x2, int x3)
{
00401730 push ebp
00401731 mov ebp,esp
00401733 sub esp,8
00401736 mov dword ptr [ebp-8],edx
00401739 mov dword ptr [ebp-4],ecx
return x1 + x2 + x3;
0040173C mov eax,dword ptr [x1]
0040173F add eax,dword ptr [x2]
00401742 add eax,dword ptr [x3]
}
00401745 mov esp,ebp
00401747 pop ebp
00401748 ret 4

从上面可以看出函数自身的区别:
_stdcall 函数结束后在返回时ret 命令带个参数,代表参数所占的栈大小,在返回时会把参数弹出栈
__fastcall 函数结束后ret 命令同样带个参数,并且它在函数入口把寄存器中的参数再放入栈中,fast 这个词不名副其实啊!

----------------------------------------------------------------------------------------------
下面,以 __cdecl 调用为例,看看函数调用前后内存栈和主要寄存器的变化
测试代码:
int __cdecl Add2(int x1, int x2, int x3)
{
int tmp[10];
return x1 + x2 + x3;
}
ret = Add2(x[0], x[1], x[2]);

1. 在ret = Add2(x[0], x[1], x[2]); 执行前
内存地址

内存中存储的值

寄存器位置

0x74H



EBP



0x68H

X[3]

0x64H

ret

ESP







2. 当执行完下面红色语句后,进入Add2 之前
ret = Add2(x[0], x[1], x[2]);
004017AF mov eax,dword ptr [ebp-4]
004017B2 push eax
004017B3 mov ecx,dword ptr [ebp-8]
004017B6 push ecx
004017B7 mov edx,dword ptr [x]
004017BA push edx
004017BB call Add2 (401750h)
004017C0 add esp,0Ch
004017C3 mov dword ptr [ret],eax

内存地址

内存中存储的值

寄存器位置

0x74H



EBP



0x68H

X[3]

0x64H

ret

0x60H

X3

0x5BH

X2

0x58H

X1

0x54H

[EIP]

ESP

可见是参数不断压入栈中,最后压入要返回时的代码地址([EIP] 的值)

3. 进入函数Add2 正式执行用户代码前,执行红色代码后
int __cdecl Add2(int x1, int x2, int x3)
{
00401750 push ebp
00401751 mov ebp,esp
00401753 sub esp,28h
int tmp[10];
return x1 + x2 + x3;
00401756 mov eax,dword ptr [x1]
00401759 add eax,dword ptr [x2]
0040175C add eax,dword ptr [x3]
}
0040175F mov esp,ebp
00401761 pop ebp
00401762 ret

内存地址

内存中存储的值

寄存器位置

0x74H





0x68H

X[3]

0x64H

ret

0x60H

X3 (Add2())

0x5BH

X2

0x58H

X1

0x54H

[EIP]

0x50H

[EBP]

EBP



0x28H

Tmp[10]

ESP

这里主要工作是保存EBP, 并设置到栈顶,这样可以通过EBP 访问该函数中的局部变量。另外,由于函数内声明了局部变量 int tmp[10] ,占用大小 10 * 4 = 28H 个字节,所以把ESP 下移。(栈总是向下扩展)

4. 然后是执行函数体代码,返回值放入EAX( 或AX,AL) 中。最后返回,返回前重设ESP,EBP 的值,通过栈中的[EIP] 的值返回到调用函数之前的代码处。
最终:
内存地址

内存中存储的值

寄存器位置

0x74H



EBP



0x68H

X[3]

0x64H

ret

ESP

0x60H

X3 ( 没用了)

0x5BH

X2 ( 没用了)

0x58H

X1 ( 没用了)

0x54H

[EIP] ( 没用了)

0x50H

[EBP] ( 没用了)



0x28H

Tmp[10] ( 没用了)

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: