经过上一篇的介绍,大家应该对调用约定有了一定的感性认识。下面通过几个实例加深一下对这五种调用约定的理解。不会查看汇编代码?很简单,在Debug模式下按Ctrl+F11就能看到汇编代码了~
1.__stdcall
源程序:
int __stdcall add(int a, int b)
{
return a + b;
}
void main()
{
add(10, 20);
}
汇编代码:
6: void main()
7: {
00401050 push ebp
00401051 mov ebp,esp
00401053 sub esp,40h
00401056 push ebx
00401057 push esi
00401058 push edi
00401059 lea edi,[ebp-40h]
0040105C mov ecx,10h
00401061 mov eax,0CCCCCCCCh
00401066 rep stos dword ptr [edi]
8: add(10, 20);
00401068 push 14h
0040106A push 0Ah
0040106C call @ILT+0(_add@8) (00401005)
9: }
00401071 pop edi
00401072 pop esi
00401073 pop ebx
00401074 add esp,40h
00401077 cmp ebp,esp
00401079 call __chkesp (00401090)
0040107E mov esp,ebp
00401080 pop ebp
00401081 ret
1: int __stdcall add(int a, int b)
2: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,40h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-40h]
0040102C mov ecx,10h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
3: return a + b;
00401038 mov eax,dword ptr [ebp+8]
0040103B add eax,dword ptr [ebp+0Ch]
4: }
0040103E pop edi
0040103F pop esi
00401040 pop ebx
00401041 mov esp,ebp
00401043 pop ebp
00401044 ret 8
注意汇编代码中红色的部分:参数自右向左入栈,函数名为_add@8,由被调用函数清理入栈参数
2.__cdecl
源程序:
int __cdecl add(int a, int b)
{
return a + b;
}
void main()
{
add(10, 20);
}
汇编代码:
6: void main()
7: {
00401060 push ebp
00401061 mov ebp,esp
00401063 sub esp,40h
00401066 push ebx
00401067 push esi
00401068 push edi
00401069 lea edi,[ebp-40h]
0040106C mov ecx,10h
00401071 mov eax,0CCCCCCCCh
00401076 rep stos dword ptr [edi]
8: add(10, 20);
00401078 push 14h
0040107A push 0Ah
0040107C call @ILT+15(_add) (00401014)
00401081 add esp,8
9: }
00401084 pop edi
00401085 pop esi
00401086 pop ebx
00401087 add esp,40h
0040108A cmp ebp,esp
0040108C call __chkesp (004010b0)
00401091 mov esp,ebp
00401093 pop ebp
00401094 ret
1: int __cdecl add(int a, int b)
2: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,40h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 lea edi,[ebp-40h]
0040102C mov ecx,10h
00401031 mov eax,0CCCCCCCCh
00401036 rep stos dword ptr [edi]
3: return a + b;
00401038 mov eax,dword ptr [ebp+8]
0040103B add eax,dword ptr [ebp+0Ch]
4: }
0040103E pop edi
0040103F pop esi
00401040 pop ebx
00401041 mov esp,ebp
00401043 pop ebp
00401044 ret
注意汇编代码中红色的部分:参数自右向左入栈,函数名为_add,被调用函数不清理入栈参数,用ret直接返回;调用函数给esp加8清理入栈参数
3.__fastcall
源程序:
int __fastcall add(int a, int b)
{
return a + b;
}
void main()
{
add(10, 20);
}
汇编代码:
6: void main()
7: {
00401060 push ebp
00401061 mov ebp,esp
00401063 sub esp,40h
00401066 push ebx
00401067 push esi
00401068 push edi
00401069 lea edi,[ebp-40h]
0040106C mov ecx,10h
00401071 mov eax,0CCCCCCCCh
00401076 rep stos dword ptr [edi]
8: add(10, 20);
00401078 mov edx,14h
0040107D mov ecx,0Ah
00401082 call @ILT+5(@add@8) (0040100a)
9: }
00401087 pop edi
00401088 pop esi
00401089 pop ebx
0040108A add esp,40h
0040108D cmp ebp,esp
0040108F call __chkesp (004010b0)
00401094 mov esp,ebp
00401096 pop ebp
00401097 ret
1: int __fastcall add(int a, int b)
2: {
00401020 push ebp
00401021 mov ebp,esp
00401023 sub esp,48h
00401026 push ebx
00401027 push esi
00401028 push edi
00401029 push ecx
0040102A lea edi,[ebp-48h]
0040102D mov ecx,12h
00401032 mov eax,0CCCCCCCCh
00401037 rep stos dword ptr [edi]
00401039 pop ecx
0040103A mov dword ptr [ebp-8],edx
0040103D mov dword ptr [ebp-4],ecx
3: return a + b;
00401040 mov eax,dword ptr [ebp-4]
00401043 add eax,dword ptr [ebp-8]
4: }
00401046 pop edi
00401047 pop esi
00401048 pop ebx
00401049 mov esp,ebp
0040104B pop ebp
0040104C ret
注意汇编代码中红色的部分:前两个参数分别送往edx和ecx,在函数中函数名为@add@8,由于参数只有两个,不涉及入栈的问题,因此不需要清理栈,直接返回
4.thiscall
源程序:
#include <stdarg.h>
class A
{
public:
int add(int a, int b);
int addMore(int a, ...);
};
int A::add(int a, int b)//固定参数
{
return a + b;
}
int A::addMore(int cnt, ...)//可变参数
{
va_list a;
int i;
int result = 0;
va_start(a, cnt);
for(i=0; i<cnt; i++)
{
result += va_arg(a, int);
}
return result;
}
void main()
{
A *a = new A();
a->add(1, 2);
a->addMore(4, 1, 2, 3, 4);
}
汇编代码:(只看main函数)
31: void main()
32: {
004010A0 push ebp
004010A1 mov ebp,esp
004010A3 sub esp,48h
004010A6 push ebx
004010A7 push esi
004010A8 push edi
004010A9 lea edi,[ebp-48h]
004010AC mov ecx,12h
004010B1 mov eax,0CCCCCCCCh
004010B6 rep stos dword ptr [edi]
33: A *a = new A();
004010B8 push 1
004010BA call operator new (00401100)
004010BF add esp,4
004010C2 mov dword ptr [ebp-8],eax
004010C5 mov eax,dword ptr [ebp-8]
004010C8 mov dword ptr [ebp-4],eax
34: a->add(1, 2);
004010CB push 2
004010CD push 1
004010CF mov ecx,dword ptr [ebp-4]
004010D2 call @ILT+10(A::add) (0040100f)
35: a->addMore(4, 1, 2, 3, 4);
004010D7 push 4
004010D9 push 3
004010DB push 2
004010DD push 1
004010DF push 4
004010E1 mov ecx,dword ptr [ebp-4]
004010E4 push ecx
004010E5 call @ILT+0(A::addMore) (00401005)
004010EA add esp,18h
36: }
004010ED pop edi
004010EE pop esi
004010EF pop ebx
004010F0 add esp,48h
004010F3 cmp ebp,esp
004010F5 call __chkesp (00401120)
004010FA mov esp,ebp
004010FC pop ebp
004010FD ret
注意汇编代码中红色的部分:对于参数固定的函数,this指针放入ecx,由被调用函数清理入栈参数;对于参数不定的函数,this指针在所有参数入栈后压入堆栈,由调用者自己清理入栈参数
5.naked call
__declspec(naked) int add(int a, int b)
{
__asm mov eax, a
__asm add eax, b
__asm ret
}
void main()
{
add(10, 20);
}
汇编代码:
8: void main()
9: {
00401030 push ebp
00401031 mov ebp,esp
00401033 sub esp,40h
00401036 push ebx
00401037 push esi
00401038 push edi
00401039 lea edi,[ebp-40h]
0040103C mov ecx,10h
00401041 mov eax,0CCCCCCCCh
00401046 rep stos dword ptr [edi]
10: add(10, 20);
00401048 push 14h
0040104A push 0Ah
0040104C call @ILT+0(_add) (00401005)
00401051 add esp,8
11: }
00401054 pop edi
00401055 pop esi
00401056 pop ebx
00401057 add esp,40h
0040105A cmp ebp,esp
0040105C call __chkesp (00401080)
00401061 mov esp,ebp
00401063 pop ebp
00401064 ret
1: __declspec(naked) int add(int a, int b)
2: {
00401020 mov eax,dword ptr [ebp+8]
3: __asm mov eax, a
4: __asm add eax, b
00401023 add eax,dword ptr [ebp+0Ch]
5: __asm ret
00401026 ret
注意汇编代码中红色的部分:不产生对寄存器的保护代码,不能用return语句返回返回值,只能插入ret汇编语句返回,返回值存放在eax中