调用约定
本文内容概括自IDA pro权威指南第66页到第69页6.2.1节调用约定
调用约定指定函数调用方放置调用函数时所需参数的具体位置(如栈中、寄存器中),此外,还约定了再函数调用结束后由谁负责从栈中删除这些参数。在使用IDA的F5功能时经常能够看到函数签名中带有cdecl
、fastcall
等字样,这些就是调用约定。调用约定是通常是特定于语言、编译器和CPU的,这里只简单了解一下主流的调用约定。
C调用约定(cdecl
)
x86体系结构的许多C编译器使用的默认调用约定叫做C调用约定(cdecl
)。
cdecl
规定:
- 调用方将所需参数放入栈中
- 参数放入顺序为从右往左v
- 调用结束后由调用方清除参数
从右往左放入参数的好处: 第一个参数永远位于栈顶,因此不管需要多少个参数都能最快找到第一个参数,非常适合可变参数的函数,如printf
要求调用方从栈中删除参数的好处: 在可变参数函数的调用时,调用方清楚的知道传入的参数数量,因此能够轻松做出调整,而被调用方无法事先知道自己会收到多少个参数。
标准调用约定(stdcall
)
标准调用约定的“标准”是由微软为自己的调用约定所起的名称stdcall
得来。
stdcall
规定:
- 调用方将所需参数放入栈中
- 参数放入顺序为从右往左
- 调用结束后由被调用方清楚参数
要求被调用方从栈中删除参数的特点: 被调用方要完成清除参数的任务,必须清楚的知道栈中有多少个参数,只有在函数参数数量固定不变时才有可能。因此,类似printf
这种可变参数的函数无法使用stdcall
。
stdcall
的优点: 在每次函数调用之后,不需要通过代码从栈中清除参数,因而能够生成体积稍小、速度稍快的程序。微软对所有共享库(DLL)文件输出的参数数量固定的函数使用stdcall
约定。
x86 fastcall
调用约定
fastcall
调用约定时stdcall
约定的一个变体。
fastcall
规定:
- 能够将最多两个参数存放在寄存器中
- 传递给函数的前两个参数将分别位于ECX和EDX寄存器中
- 剩余参数以类似于stdcall约定的方式从右往左放入栈上
C 调用约定(thiscall
)
C 类中的非静态成员函数与标准函数不同,需要使用this
指针,该指针指向用于调用函数的对象。用于调用函数的对象的地址必须由调用方提供,因此,他在调用非静态成员函数时作为参数提供。C 语言标准未规定应如何向非静态成员函数传递this
指针,因此,不同编译器使用不同的技巧来传递this
指针。
其他调用约定
略