_cdecl、_stdcall和_fastcall的区别

概述

在windows开发环境中有三种觉的调用协议,分别为

  • _cdecl C调用
  • _stdcall 标准调用
  • _fastcall 快速调用
    三种调用方式有参数传递和栈帧恢复的方式有所不同,本文在结合汇编代码简要说明一下有三种调用方式的区别。

环境

  • windows xp sp3
  • vc6.0
    在VC中可以使用_cdecl、_stdcall、_fastcall来指定函数的调用方式,默认为_cdecl

_cdecl

_cdecl 使用的栈来传递参数,从右向左将参数入栈,需要调用者要恢复栈
下面在vc环境中的看一下_cdecl的汇编代码
在vc6.0 新建一个控制台空工程
在VC6.0中新建空的控制台工程
新建一个简单的C程序,其中add函数的调用方式显式的指定为_cdecl

_cdecl int add(int a, int b, int c, int d)
{
    
    
	int local1;
	int local2 = 0;
	int sum = 0;
	sum = a + b + c + d;
	return sum;
}

int main()
{
    
    
	int local1;
	int local2 = 0;
	int sum = 0;
	sum = add(1,2,3,4);
	return 0;
}

在 add的调用处设置断点,编译生成后的运行显示出反汇编窗口
设置断点显示反汇编视图
main函数的汇编

--- c:\program files\microsoft visual studio\myprojects\1\1.cpp  --------------------------------------------------------------------------------------------------------------
10:
11:   int main()
12:   { 保存现场
00401070 55                   push        ebp
00401071 8B EC                mov         ebp,esp
00401073 83 EC 4C             sub         esp,4Ch
00401076 53                   push        ebx
00401077 56                   push        esi
00401078 57                   push        edi
00401079 8D 7D B4             lea         edi,[ebp-4Ch] ;在栈上分别局部变量空间
0040107C B9 13 00 00 00       mov         ecx,13h
00401081 B8 CC CC CC CC       mov         eax,0CCCCCCCCh   ;写入int 3
00401086 F3 AB                rep stos    dword ptr [edi]
13:       int local1;   //局部变量local1在 ebp-4 处
14:       int local2 = 0;  //局部变量local2在 ebp-8 处
00401088 C7 45 F8 00 00 00 00 mov         dword ptr [ebp-8],0
15:       int sum = 0; //局部变量sum中 ebp-12 处
0040108F C7 45 F4 00 00 00 00 mov         dword ptr [ebp-0Ch],0
16:       sum = add(1,2,3,4); //注意_cdecl的传参方式,从右向左压栈的方式传参
00401096 6A 04                push        4
00401098 6A 03                push        3
0040109A 6A 02                push        2
0040109C 6A 01                push        1
0040109E E8 62 FF FF FF       call        @ILT+0(add) (00401005)  ;call指令 将函数的返回地址(下一行指令地址004010A3)压入栈,跳转到00401005 E9 16 00 00 00        jmp         add (00401020)
004010A3 83 C4 10             add         esp,10h  ;调用完成后,恢复栈帧 esp + 4*4 这是参数占用的栈空间
004010A6 89 45 F4             mov         dword ptr [ebp-0Ch],eax ;将eax的值赋值给sum
17:       return 0;
004010A9 33 C0                xor         eax,eax eax=0
18:   }  恢复现场
004010AB 5F                   pop         edi
004010AC 5E                   pop         esi
004010AD 5B                   pop         ebx
004010AE 83 C4 4C             add         esp,4Ch
004010B1 3B EC                cmp         ebp,esp
004010B3 E8 18 00 00 00       call        __chkesp (004010d0)
004010B8 8B E5                mov         esp,ebp
004010BA 5D                   pop         ebp
004010BB C3                   ret
--- No source file  -----------------------------------------------------------------------------------------------------------------------------------------------------------

add函数的反汇编代码

--- c:\program files\microsoft visual studio\myprojects\1\1.cpp  ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1:
2:    _cdecl int add(int a, int b, int c, int d)
3:    { //保存现场 此时 栈项保存着返回地址
00401020 55                   push        ebp ;保存ebp
00401021 8B EC                mov         ebp,esp ;
00401023 83 EC 4C             sub         esp,4Ch ;扩展栈空间,用来存储局部变量 76个字节
00401026 53                   push        ebx
00401027 56                   push        esi
00401028 57                   push        edi
00401029 8D 7D B4             lea         edi,[ebp-4Ch] ;在局部变量的存储空间中写入int 3
0040102C B9 13 00 00 00       mov         ecx,13h ;13h=19
00401031 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
00401036 F3 AB                rep stos    dword ptr [edi]
4:        int local1; //存储在ebp-4
5:        int local2 = 0; //存储在ebp-8
00401038 C7 45 F8 00 00 00 00 mov         dword ptr [ebp-8],0
6:        int sum = 0; //存储在ebp-12 处
0040103F C7 45 F4 00 00 00 00 mov         dword ptr [ebp-0Ch],0
7:        sum = a + b + c + d;
00401046 8B 45 08             mov         eax,dword ptr [ebp+8]
00401049 03 45 0C             add         eax,dword ptr [ebp+0Ch]
0040104C 03 45 10             add         eax,dword ptr [ebp+10h]
0040104F 03 45 14             add         eax,dword ptr [ebp+14h]
00401052 89 45 F4             mov         dword ptr [ebp-0Ch],eax
8:        return sum;
00401055 8B 45 F4             mov         eax,dword ptr [ebp-0Ch] ;将sum的值给eax,eax用来保存函数的返回值
9:    } //恢复现场
00401058 5F                   pop         edi
00401059 5E                   pop         esi
0040105A 5B                   pop         ebx
0040105B 8B E5                mov         esp,ebp
0040105D 5D                   pop         ebp
0040105E C3                   ret ; 从栈项弹出返回地址,跳转到返回地址处
--- No source file  ------------------------------------------------------

stdcall

stdcall 也使用栈从右至左来传递参数,在函数内部恢复栈帧 windows api一般采用这个方式
在上面的代码修改为如下


_stdcall int add(int a, int b, int c, int d)
{
    
    
	int local1;
	int local2 = 0;
	int sum = 0;
	sum = a + b + c + d;
	return sum;
}

int main()
{
    
    
	int local1;
	int local2 = 0;
	int sum = 0;
	sum = add(1,2,3,4);
	return 0;
}

汇编代码为

--- C:\Program Files\Microsoft Visual Studio\MyProjects\1\1.cpp  ------------------------
1:
2:    _stdcall int add(int a, int b, int c, int d)
3:    {
00401020 55                   push        ebp
00401021 8B EC                mov         ebp,esp
00401023 83 EC 4C             sub         esp,4Ch
00401026 53                   push        ebx
00401027 56                   push        esi
00401028 57                   push        edi
00401029 8D 7D B4             lea         edi,[ebp-4Ch]
0040102C B9 13 00 00 00       mov         ecx,13h
00401031 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
00401036 F3 AB                rep stos    dword ptr [edi]
4:        int local1;
5:        int local2 = 0;
00401038 C7 45 F8 00 00 00 00 mov         dword ptr [ebp-8],0
6:        int sum = 0;
0040103F C7 45 F4 00 00 00 00 mov         dword ptr [ebp-0Ch],0
7:        sum = a + b + c + d;
00401046 8B 45 08             mov         eax,dword ptr [ebp+8]
00401049 03 45 0C             add         eax,dword ptr [ebp+0Ch]
0040104C 03 45 10             add         eax,dword ptr [ebp+10h]
0040104F 03 45 14             add         eax,dword ptr [ebp+14h]
00401052 89 45 F4             mov         dword ptr [ebp-0Ch],eax
8:        return sum;
00401055 8B 45 F4             mov         eax,dword ptr [ebp-0Ch]
9:    }
00401058 5F                   pop         edi
00401059 5E                   pop         esi
0040105A 5B                   pop         ebx
0040105B 8B E5                mov         esp,ebp
0040105D 5D                   pop         ebp
0040105E C2 10 00             ret         10h ;ret n指令将 返回地址(004010A3) 从栈顶弹出,再将esp+4*4 跳转到返回地址 在函数内部恢复栈帧
--- No source file  ---------------------------------------------------------------------
...
--- C:\Program Files\Microsoft Visual Studio\MyProjects\1\1.cpp  ------------------------
10:
11:   int main()
12:   {
00401070 55                   push        ebp
00401071 8B EC                mov         ebp,esp
00401073 83 EC 4C             sub         esp,4Ch
00401076 53                   push        ebx
00401077 56                   push        esi
00401078 57                   push        edi
00401079 8D 7D B4             lea         edi,[ebp-4Ch]
0040107C B9 13 00 00 00       mov         ecx,13h
00401081 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
00401086 F3 AB                rep stos    dword ptr [edi]
13:       int local1;
14:       int local2 = 0;
00401088 C7 45 F8 00 00 00 00 mov         dword ptr [ebp-8],0
15:       int sum = 0;
0040108F C7 45 F4 00 00 00 00 mov         dword ptr [ebp-0Ch],0
16:       sum = add(1,2,3,4);
00401096 6A 04                push        4
00401098 6A 03                push        3
0040109A 6A 02                push        2
0040109C 6A 01                push        1
0040109E E8 6C FF FF FF       call        @ILT+10(add) (0040100f)
004010A3 89 45 F4             mov         dword ptr [ebp-0Ch],eax ;调用完add函数,不需要恢复栈帧
17:       return 0;
004010A6 33 C0                xor         eax,eax
18:   }
004010A8 5F                   pop         edi
004010A9 5E                   pop         esi
004010AA 5B                   pop         ebx
004010AB 83 C4 4C             add         esp,4Ch
004010AE 3B EC                cmp         ebp,esp
004010B0 E8 1B 00 00 00       call        __chkesp (004010d0)
004010B5 8B E5                mov         esp,ebp
004010B7 5D                   pop         ebp
004010B8 C3                   ret
--- No source file  ---------------------------------------------------------------------

fastcall

fastcall将使用寄存器和栈结合的方式传递参数,ecx为第一个参数,edx为第二个参数,其余的参数从右向左入栈
将上面的代码改为


_fastcall int add(int a, int b, int c, int d)
{
    
    
	int local1;
	int local2 = 0;
	int sum = 0;
	sum = a + b + c + d;
	return sum;
}

int main()
{
    
    
	int local1;
	int local2 = 0;
	int sum = 0;
	sum = add(1,2,3,4);
	return 0;
}

汇编代码为

--- C:\Program Files\Microsoft Visual Studio\MyProjects\1\1.cpp  ------------------------
1:
2:    _fastcall int add(int a, int b, int c, int d)
3:    {
00401020 55                   push        ebp
00401021 8B EC                mov         ebp,esp
00401023 83 EC 54             sub         esp,54h
00401026 53                   push        ebx
00401027 56                   push        esi
00401028 57                   push        edi
00401029 51                   push        ecx
0040102A 8D 7D AC             lea         edi,[ebp-54h]
0040102D B9 15 00 00 00       mov         ecx,15h
00401032 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
00401037 F3 AB                rep stos    dword ptr [edi]
00401039 59                   pop         ecx
0040103A 89 55 F8             mov         dword ptr [ebp-8],edx
0040103D 89 4D FC             mov         dword ptr [ebp-4],ecx
4:        int local1;
5:        int local2 = 0;
00401040 C7 45 F0 00 00 00 00 mov         dword ptr [ebp-10h],0
6:        int sum = 0;
00401047 C7 45 EC 00 00 00 00 mov         dword ptr [ebp-14h],0
7:        sum = a + b + c + d;
0040104E 8B 45 FC             mov         eax,dword ptr [ebp-4]
00401051 03 45 F8             add         eax,dword ptr [ebp-8]
00401054 03 45 08             add         eax,dword ptr [ebp+8]
00401057 03 45 0C             add         eax,dword ptr [ebp+0Ch]
0040105A 89 45 EC             mov         dword ptr [ebp-14h],eax
8:        return sum;
0040105D 8B 45 EC             mov         eax,dword ptr [ebp-14h]
9:    }
00401060 5F                   pop         edi
00401061 5E                   pop         esi
00401062 5B                   pop         ebx
00401063 8B E5                mov         esp,ebp
00401065 5D                   pop         ebp
00401066 C2 08 00             ret         8 ;从栈顶弹出返回地址(004010A9),将esp+4*4,跳转到返回地址
--- No source file  ---------------------------------------------------------------------
...
--- C:\Program Files\Microsoft Visual Studio\MyProjects\1\1.cpp  ------------------------
10:
11:   int main()
12:   {
00401070 55                   push        ebp
00401071 8B EC                mov         ebp,esp
00401073 83 EC 4C             sub         esp,4Ch
00401076 53                   push        ebx
00401077 56                   push        esi
00401078 57                   push        edi
00401079 8D 7D B4             lea         edi,[ebp-4Ch]
0040107C B9 13 00 00 00       mov         ecx,13h
00401081 B8 CC CC CC CC       mov         eax,0CCCCCCCCh
00401086 F3 AB                rep stos    dword ptr [edi]
13:       int local1;
14:       int local2 = 0;
00401088 C7 45 F8 00 00 00 00 mov         dword ptr [ebp-8],0
15:       int sum = 0;
0040108F C7 45 F4 00 00 00 00 mov         dword ptr [ebp-0Ch],0
16:       sum = add(1,2,3,4);
00401096 6A 04                push        4 ;第四个参数
00401098 6A 03                push        3 ;第三个参数
0040109A BA 02 00 00 00       mov         edx,2 ;第二个参数
0040109F B9 01 00 00 00       mov         ecx,1 ;第一个参数
004010A4 E8 6B FF FF FF       call        @ILT+15(add) (00401014) ;将地址004010A9压入栈,跳转到00401014处
004010A9 89 45 F4             mov         dword ptr [ebp-0Ch],eax
17:       return 0;
004010AC 33 C0                xor         eax,eax
18:   }
004010AE 5F                   pop         edi
004010AF 5E                   pop         esi
004010B0 5B                   pop         ebx
004010B1 83 C4 4C             add         esp,4Ch
004010B4 3B EC                cmp         ebp,esp ;比较ebp和esp的值
004010B6 E8 15 00 00 00       call        __chkesp (004010d0)
004010BB 8B E5                mov         esp,ebp
004010BD 5D                   pop         ebp
004010BE C3                   ret
--- No source file  ---------------------------------------------------------------------
          

__chkesp的作用

chkesp用来实现堆栈检查
将上面的程序修改为

_fastcall int add(int a, int b, int c, int d)
{
    
    
	int local1;
	int local2 = 0;
	int sum = 0;
	sum = a + b + c + d;
	return sum;
}

int main()
{
    
    
	int local1;
	int local2 = 0;
	int sum = 0;
	sum = add(1,2,3,4);
	_asm mov ebp,0 //修改ebp的值,产生错误
	return 0;
}

__chkesp函数 当ebp和esp的值不一致时,会调用_CrtDbgReport 函数,弹出报错的窗口

__chkesp:
004010D0 75 01                jne         __chkesp+3 (004010d3)
004010D2 C3                   ret
004010D3 55                   push        ebp
004010D4 8B EC                mov         ebp,esp
004010D6 83 EC 00             sub         esp,0
004010D9 50                   push        eax
004010DA 52                   push        edx
004010DB 53                   push        ebx
004010DC 56                   push        esi
004010DD 57                   push        edi
004010DE 68 30 20 42 00       push        offset string "The value of ESP was not properl"... (00422030)
004010E3 68 2C 20 42 00       push        offset string "" (0042202c)
004010E8 6A 2A                push        2Ah
004010EA 68 1C 20 42 00       push        offset string "i386\\chkesp.c" (0042201c)
004010EF 6A 01                push        1
004010F1 E8 BA 02 00 00       call        _CrtDbgReport (004013b0)
004010F6 83 C4 14             add         esp,14h
004010F9 83 F8 01             cmp         eax,1
004010FC 75 01                jne         __chkesp+2Fh (004010ff)
004010FE CC                   int         3
004010FF 5F                   pop         edi
00401100 5E                   pop         esi
00401101 5B                   pop         ebx
00401102 5A                   pop         edx
00401103 58                   pop         eax
00401104 8B E5                mov         esp,ebp
00401106 5D                   pop         ebp
00401107 C3                   ret

报错

猜你喜欢

转载自blog.csdn.net/a854596855/article/details/121156193
今日推荐