函数调用协议汇总X86/X64/ARM/ARM64
本文中文字说明及图片所示的用例二进制文件下载链接:
二进制用例链接
X86 函数调用约定
函数调用协议会影响函数参数的入栈方式、栈内数据的清除方式、编译器函数名的修饰规则。如下图所示,可以在IDE环境中设定所有函数默认的调用协议,还可以在函数定义时单独设定本函数的调用协议。
调用协议常用场合
- __stdcall:Windows API默认的函数调用协议。
- __cdecl:C/C++默认的函数调用协议。
- __fastcall:适用于对性能要求较高的场合。
函数参数入栈方式
-
__stdcall:函数参数由右向左入栈。
-
__cdecl:函数参数由右向左入栈。
-
__fastcall:从左开始不大于4字节的参数放入CPU的ECX和EDX寄存器,其余参数从右向左入栈。
栈内数据清除方式
- __stdcall:函数调用结束后由被调用函数清除栈内数据。
- __cdecl:函数调用结束后由函数调用者清除栈内数据。
- __fastcall:函数调用结束后由被调用函数清除栈内数据。
参考图同__stndcall
C语言编译器函数名称修饰规则
“functionname”为函数名,“number”为参数字节数
- __stdcall:编译后,函数名被修饰为“[email protected]”。
- __cdecl:编译后,函数名被修饰为“_functionname”。
- __fastcall:编译后,函数名给修饰为“@[email protected]”。
C++语言编译器函数名称修饰规则
“@@”+YG,YA,YI标识参数表的开始,后跟参数表
参数表以代号表示:
X–void ,
D–char,
E–unsigned char,
F–short,
H–int,
I–unsigned int,
J–long,
K–unsigned long,
M–float,
N–double,
_N–bool,
…
PA–表示指针,后面的代号表明指针类型,如果相同类型的指针连续出现,以“0”代替,一个“0”代表一次重复;
- _stdcall:编译后,函数名被修饰“[email protected]@YG******@Z”。
- __cdecl:编译后,函数名被修饰为“[email protected]@YA******@Z”
- __fastcall:编译后,函数名被修饰为“[email protected]@YI******@Z”
X64 函数调用约定
X64只有一种 fastcall 函数调用约定
参数1、参数2、参数3、参数4分别保存在 RCX、RDX、R8D、R9D ,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 RAX 中。
vc调用的传参方式。前4个参数使用rcx,rdx,r8,r9,之后的参数使用堆栈。
GCC前6个参数使用rdi、rsi、rdx、rcx、r8、r9,剩下的参数用栈。注意rdx、rcx的顺序和MSVC上不一样。
ARM/ARM64 函数调用约定
ARM和ARM64使用的是ATPCS(ARM-Thumb Procedure Call Standard/ARM-Thumb过程调用标准)的函数调用约定。
ARM
参数1~参数4 分别保存到 R0~R3 寄存器中 ,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 R0 中。
ARM64
参数1~参数8 分别保存到 X0~X7 寄存器中 ,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 X0 中。
thiscall 函数调用约定
x86
参数从右往左依次入栈,this指针存放ECX中,被调用者实现栈平衡,返回值存放在 EAX 中。
X64
参数1、参数2、参数3分别保存在RDX、R8D、R9D中,this指针存放RCX中,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 RAX 中。
ARM
参数1、参数2、参数3分别保存在R1、R2、R3中,this指针存放R0中,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 R0 中。
ARM64
参数1~参数7 分别保存到 X1~X7 寄存器中,this指针存放X0中,剩下的参数从右往左依次入栈,被调用者实现栈平衡,返回值存放在 X0 中。