函数调用约定

函数调用约定(Calling Convention)是对函数调用时如何传递参数的一种约定。

函数调用完毕后,ESP寄存器的值需要恢复到函数调用之前的值,从而保证可引用的栈大小不会缩减。而函数调用约定就是解决函数调用后如何处理ESP的问题的。

1、cdecl

cdecl主要是C语言中使用的方式,调用者负责处理栈。

编写简易的代码如下:

#include "stdio.h"

int add(int a, int b){
	return (a + b);
}

int main(){
	return add(1, 2);
}

在编译生成exe文件前先关闭优化选项才能更好地适用栈帧:


选择Release,生成exe文件,用OllyDbg打开,拉到最上面即可看到主程序:


可以看到,011D101C地址处的“add esp 8”指令,该命令直接清理其压入栈中的两个函数参数(ESP加8即ESP向下移动两个位置,如下图所示,ESP以上位置的数据将被改写而不用特意去释放内存),这样的方式称为cdecl。


cdecl方式的好处在于,可以向被调用函数传递长度可变的参数,而其他的调用约定很难实现。


2、stdcall

stdcall常用于Win32 API,由被调用者清理栈。若想使用该方式编译源码,只需使用_stdcall关键字即可。

编写简易的代码如下:

#include "stdio.h"

int _stdcall add(int a, int b){
	return (a + b);
}

int main(){
	return add(1, 2);
}

用OllyDbg打开:


可以看到,较cdecl方式相比,stdcall方式在main()函数处省略了清理栈的代码(add esp,8),而栈的清理工作由add()函数最后的“retn 8”来完成,其相当于“RETN + POP 8字节”,即返回后使ESP增加到指定大小。

stdcall方式的好处在于,被调用者函数内部存在着栈清理代码,与每次调用函数时都要用ADD ESP,XXX命令的cdecl方式相比,代码尺寸要小。


3、fastcall

fastcall方式与stdcall方式类似,但该方法通常会使用寄存器(而非栈内存)去传递哪些需要传递给函数的部分参数。其优势在于,可以实现对函数的快速调用(从CPU的立场看,访问寄存器的速度要远比内存的快得多)。

猜你喜欢

转载自blog.csdn.net/ski_12/article/details/80549743