Linuxer
人生的乐趣有多种:爱、享受、冒险等等,还有发现和创造。能享受后两种乐趣的人并不多。做事应该带着发现的心态而不是争强好胜的心态。

导航

 


TAG: 调用惯例, cdecl, stdcall, fastcall, thiscall 
DATE: 2013-08-06

什么是调用惯例

调用惯例(Calling Conventions)指计算机程序执行时调用函数或过程的一些约定,包括:

  1. 函数的参数是通过栈还是寄存器传递?
  2. 如果通过栈传递,顺序是怎样的,是从左至右入栈还是相反。
  3. 谁负责清理栈,是调用者还是被调用者?

从清理栈的角度来讲,调用惯例可分为三类:函数的调用者清理,函数清理,混合清理(有时由调用者清理,有时由函数自己清理)。

调用者清理

著名的cdecl就是由函数调用者清理栈的调用惯例。 cdecl是基于c语言的调用惯例,也是x86机器上大多数C编译器采用的调用惯例。

函数的返回结果多通过EAX寄存器返回。 对于32位机器,EAX能容纳4个字节。 整数或内存地址(指针),通过EAX寄存器返回是没有问题的。 超过4个字节的结构体呢?如何返回?

通过阅读 http://en.wikipedia.org/wiki/X86_calling_conventions,我找到了答案。 对于较小的结构体或对象,可以通过EAX:EDX寄存器对返回。 对于超大的对象或结构体,caller在调用函数之前会分配出内存空间,然后把这个空间地址作为第一个参数隐式地传给函数。被调用的函数callee把结果写进这片内存空间,再pop空间地址,之后才返回。 对于浮点数的结果,似乎是通过 ST0 x87 register(浮点寄存器)返回的。

因为调用者知道为参数分配了多少栈空间,所以由调用者清理栈就有一个好处: 为参数分配的栈空间大小可以动态决定。 因此cdecl支持可变参数的函数的调用,例如printf

如果强迫某个函数使用cdecl调用惯例,可以在函数声明中加_cdecl关键字,如:

void _cdecl funct();

函数自己清理

pascalstdcallfastcall都是由函数来清理栈。 通过阅读程序的汇编代码,可以很容易识别这类调用惯例。因为函数返回前会清理栈。

pascal是基于PASCAL编程语言的函数调用惯例。 参数按照从左到右的顺序压栈(和cdecl的入栈顺序相反)。 OS/2 1.x,Microsoft Windows 3.x 和 Borland Delphi 1.x中的16位API都使用这种函数调用惯例。

stdcall是从pascal调用惯例演变出来的,和pascal不同的是,stdcall以从右到左的顺序对参数压栈。 返回值存储在EAX寄存器中。Win32 API就是采用的这种调用惯例。

fastcall是混合使用寄存器和栈来存储函数的参数,比如把前两个参数存储在寄存器中,其余的参数入栈。 有Microsoft fastcallBorland fastcall等不同的实现。

由函数自己清理栈的好处在于:调用者不需要每次调用函数之后都清理栈,从而节省了不少代码, 从而生成的二进制文件比较小。坏处在于,由于清理栈的代码是事先生成在函数体内, 所以不能支持可变参数的函数。

混合清理

混合清理的代表是thiscall,对C++中非静态成员函数使用的就是这种调用惯例。

对于gcc编译器来说,thiscall几乎和cdecl相同:函数调用者负责清理栈,参数按从右到左的顺序入栈。 不同的是,thiscall最后会把this指针压栈,就好象它是函数的第一个参数。(其实也是的吧)

对于Microsoft VC++编译器,thiscall类似于Windows API的stdcall,函数的参数从右到左压栈,由参数来清理栈。和stdcall不同的是,thiscall会通过ECX寄存器来传递this指针。因为由函数自己清理栈不支持可变参数的函数调用,所以对于可变参数的函数,则由函数的调用者来清理栈。这是thiscall的灵活之处。

总结

调用惯例出栈方参数传递名字修饰
cdecl 函数调用方 从右至左的顺序压参数入栈 下划线+函数名
pascal 函数本身 从左至右的顺序入栈 较为复杂,参见pascal文档
stdcall 函数本身 从右至左的顺序压参数入栈 下划线+函数名+@+参数的字节数, 如函数 int func(int a, double b)的修饰名是 _func@12
fastcall 函数本身 头两个 DWORD(4字节)类型或者更少字节的参数 被放入寄存器,其他剩下的参数按从右至左的顺序入栈 @+函数名+@+参数的字节数
thiscall 不一定 从右至左的顺序压参数入栈(有时会通过寄存器传递this指针) 不详
posted on 2013-08-06 21:05  JollyWing  阅读(1954)  评论(0编辑  收藏  举报