x86平台下调用约定  

  我们都知道x86平台下常用的有三种调用约定,__cdecl、__stdcall、__fastcall。我们分别对这三种调用约定进行分析。

  __cdecl

  __cdecl是C/C++的默认调用约定,如果不显示声明调用约定的情况下,就是该调用约定。下面我们来从汇编层次来熟悉这种调用约定。

我写了一个函数,如下:

 int __cdecl TestCdecl(int a, int b, int c, int d, int e)
{
return a + b + c + d + e;
}

  对了,给大家说一下,VS如何进入反汇编。我们在调试界面下,按住Alt+8键就可以进入反汇编窗口了。

  可以看出__cdecl参数从右至左入栈,然后由调用者(caller)清理栈区。

  __stdcall

  __stdcall是Windows API默认调用约定,微软的WINAPI、CALLBACK等宏都是这个调用约定,我还是写了个例子来看一下。

 int __stdcall TestStdcall(int a, int b, int c, int d, int e)
{
return a + b + c + d + e;
}

  我们继续来看一下反汇编。

  可以看到,main函数并没有对子函数的清栈过程,__stdcall参数跟__cdecl入栈方式相同,从右至左入栈,被调用者(callee)清理栈区。

  __fastcall

  __fastcall是32位下一种特殊的调用约定,我慢还是通过代码和反汇编看看它到底特殊在哪。

 int __fastcall TestFastcall(int a, int b, int c, int d, int e)
{
return a + b + c + d + e;
}

  我们可以看到,__fastcall参数还是从右至左入栈,但是不同的是前两个参数被分别放进了ecx、edx寄存器,如果少于或等于两个参数,会先将参数放入寄存器。同样由被调用者(callee)清理栈区。

x64平台下调用约定

  我们再来看一下64位情况下调用约定。同样我们测试声明为__cdecl的调用约定。反汇编结果如下:

  我们看到这个入栈方式怎么跟32位下fastcall的入栈很像。这时候查一下CSDNhttps://www.microsoft.com/china/MSDN/library/Windev/64bit/issuesx64.mspx?pf=true

发现有如下一句话:在设计调用约定时,x64 体系结构利用机会清除了现有 Win32 调用约定(如 __stdcall、__cdecl、__fastcall、_thiscall 等)的混乱。在 Win64 中,只有一个本机调用约定和 __cdecl 之类的修饰符被编译器忽略。除此之外,减少调用约定行为还为可调试性带来了好处。

  原来64位平台下只有一种变形的__fastcall的调用约定,前4参数则先放入ecx、edx、r8、r9寄存器,更多的参数放入栈区。这个时候我们要注意的是,在64位下,系统还是为前4个参数预留了栈区空间(每个栈空间大小为8字节,共32字节大小),然后将基存器的值放入所预留的栈区空间。为什么系统要多此一举呢?我们都知道寄存器传递参数速度要远大于栈区传值,而将寄存器中的值再放入栈区预留空间,这是为了防止在传递参数的过程中,寄存器需要接收其他的值而导致参数无法传递,或者其他值无法接收的情况。

  另外我们还看到CSDN上说64位下__fastcall由调用者(caller)清理栈区空间。但是我们为什么没有看见main()函数清理子函数栈空间的过程呢?

  这是由于64位平台下栈区空间开辟问题导致。我们还在CSDN上看到这样一句话:与通过 PUSH 和 POP 指令在堆栈中显式添加和移除参数的 x86 编译器不同,x64 代码生成器会预留足够的堆栈空间,以调用最大目标函数(参数方法)所使用的任何内容。随后,在调用子函数时,它重复使用相同的堆栈区域来设置参数。

  这句话什么意思呢?它的意思就是我们在64位下一开始系统会为main()函数开辟一个很大的栈区,但是main()函数并未消耗掉这么大的栈区空间,这时候怎么办呢?子函数就会还继续利用main()函数的栈区空间,所以main()函数并不用对子函数栈区空间进行清理。

参数列表

  这时候我们再来看一下一些特殊的问题,在C/C++下有一个可变长参数列表的概念。我们来看一下在这种情况下,调用约定又有什么变化。

x86下参数列表

  按照惯例先看代码

 int __fastcall Test(unsigned int n, ...)
{
int sum = ;
va_list args;
va_start(args, n);
while (n>)
{
//通过va_arg(args,int)依次获取参数的值
sum += va_arg(args, int);
n--;
}
va_end(args);
return sum;
}

  再来看看反汇编:

  我们可以看到,这时候__fastcall已经退化为__cdecl了,其实其他调用约定也一样,在32位平台下全部会退化为__cdecl。

x64平台下调用约定

  我们再将函数编译为64位,反编译如下:

  这个还是64位下默认的特殊的__fastcall调用约定。至此调用约定相关问题已基本说完了,小弟不才,有什么不对的地方还请指正。

jpg改rar 

x86 x64下调用约定浅析的更多相关文章

  1. x64的调用约定

    在设计调用约定时,x64 体系结构利用机会清除了现有 Win32 调用约定(如 __stdcall.__cdecl.__fastcall._thiscall 等)的混乱.在 Win64 中,只有一个本 ...

  2. X86/X64 函数调用约定

    C 语言有 __cdecl.__stdcall.__fastcall.naked.__pascal. C++ 语言有 __cdecl.__stdcall.__fastcall.naked.__pasc ...

  3. x86和x64下指针的大小

    根据测试 int main() { ; )); )); int n1 = sizeof(a); int n2 = sizeof(p); // int n3 = sizeof(*p); error in ...

  4. .net下com调用支持x86/x64

    起因 项目涉及u3d/wpf端的渲染图形合成,采用了开源项目spout,为了便捷,采用了spout的com版本作为c#端的调用 项目调整后,细节已经捋清楚了. 但是考虑桌面应用采用anypc,根据运行 ...

  5. x64 stack walking、调用约定、函数参数识别

    k = <rsp> <rip> <frame_count>x64下manual stack walking与x86不同,x86一般情况下有ebp chain,x64 ...

  6. X86调用约定 calling convention

    http://zh.wikipedia.org/wiki/X86%E8%B0%83%E7%94%A8%E7%BA%A6%E5%AE%9A 这里描述了在x86芯片架构上的调用约定(calling con ...

  7. x64汇编第三讲,64位调用约定与函数传参.

    目录 x64汇编第三讲,64位调用约定与函数传参. 一丶复习X86传参 二丶x64汇编 2.1汇编详解 x64汇编第三讲,64位调用约定与函数传参. 一丶复习X86传参 在x86下我们汇编的传参如下: ...

  8. C C++ ARM X86 函数 方法 的调用约定

    参考:https://bbs.pediy.com/thread-224583.htm 整理成表格方便查询 cdecl(C规范), stdcall(WinAPI默认), fastcall, ATPCS( ...

  9. VS2012在win7 64位机中x86和x64下基本类型的占用空间大小(转)

    VS2012在win7 64位机中x86和x64下基本类型的占用空间大小 #include "stdafx.h" #include <windows.h> int _t ...

随机推荐

  1. Linux kernel 生成 /dev/spidev*

    最近在调试一个 spi 转 4 路uart 的设备 先从spi 底层开始做起 kernel make menuconfig Device Drivers ---> [*] SPI support ...

  2. win10系统中UserManager 总是被禁用怎么解决?

    RT,也就是提示win10开始菜单和cortana无法工作.升级win10后一直被这个问题困扰,论坛也见有人发帖求助这类问题,百度了方法打开任务管理器进入服务更改User Manager启动类型为自动 ...

  3. JavaScript(二):JavaScript语法及数据类型

    一.JavaScript语法 1.区分大小写ECMAScript中的一切,包括变量.函数名和操作符都是区分大小写的.例如:text和Text表示两种不同的变量.2.标识符所谓标识符,就是指变量.函数. ...

  4. 一、VM8.0 + CentOS 6.5 连接网络

    VM8.0 + CentOS 6.5 连接网络

  5. android抓包工具——使用fiddler4在安卓手机抓包

    Fiddler是一款非常流行并且实用的http抓包工具,它的原理是在本机开启了一个http的代理服务器,然后它会转发所有的http请求和响应,因此,它比一般的firebug或者是chrome自带的抓包 ...

  6. Droptiles - 炫酷的 Metro 风格的层叠式 Web 面板

    介绍 Droptiles是一套Metro风格的类似Win8的Web2.0控制面板.它采用图块(tiles)建立用户体验.图块(tiles)是一些可以从外部资源中获取数据的迷你应用.点击图块(tile) ...

  7. Json---Windows下使用Jsoncpp

    上述Json解析使用的是Jsoncpp,要使用Jsoncpp,得做如下几步的配置: 1. 首先从http://sourceforge.net/projects/jsoncpp/下载,压缩包大约105k ...

  8. udp编程中,一次能发送多少个bytes为好?

    在进行UDP编程的时候,我们最容易想到的问题就是,一次发送多少bytes好? 当然,这个没有唯一答案,相对于不同的系统,不同的要求,其得到的答案是不一样的,我这里仅对    像ICQ一类的发送聊天消息 ...

  9. Eclipse最经常使用快捷键总结

     1. ctrl+shift+r:打开资源 这可能是全部快捷键组合中最省时间的了. 这组快捷键能够让你打开你的工作区中不论什么一个文件,而你仅仅须要按下文件名称或mask名中的前几个字母,比方appl ...

  10. php判断来访者是否是搜索引擎的蜘蛛

    我们可以通过HTTP_USER_AGENT来判断是否是蜘蛛,搜索引擎的蜘蛛都有自己的独特标志,下面列取了一部分. function is_crawler() { $userAgent = strtol ...