前言
网上大多教程都是对x86汇编进行分析,少有x64的,因此,本次对x86和x64的函数调用的参数传递以及函数的调用约定进行详细的分析对比和总结。
(cdecl,fastcall,stdcall,vectorcall,thiscall)
注意本次实验中(环境):x86的cdecl, fastcall, stdcall代码以debug模式编译,x86的thiscall,以及x64代码均以relase模式下编译并关闭性能优化
x86 汇编分析 (cdecl, fastcall, stdcall,thiscall)
分析汇编的源码:
#include <iostream> int __cdecl cdecl_test(int a, int b, int c, int d, int e, int f, int g) { char p[] = "Hello, World!"; int result = 0; result += a + b + c + d + e + f + g; return result; } int __fastcall fastcall_test(int a, int b, int c, int d, int e, int f, int g) { char p[] = "Hello, World!"; int result = 0; result += a + b + c + d + e + f + g; return result; } int __stdcall stdcall_test(int a, int b, int c, int d, int e, int f, int g) { char p[] = "Hello, World!"; int result = 0; result += a + b + c + d + e + f + g; return result; } int main(){ int a = 1, b = 2, c = 3, d = 4, e = 5, f = 6, g = 7; int result = 0; result = cdecl_test(a, b, c, d, e, f, g); printf("cdecl_test result: %dn", result); result = fastcall_test(a, b, c, d, e, f, g); printf("fastcall_test result: %dn", result); result = stdcall_test(a, b, c, d, e, f, g); printf("stdcall_test result: %dn", result); return 0; }
#include <iostream> class Test { public: int func(int a, int b, int c, int d, int e, int f, int g) { int result = 0; result += a + b + c + d + e + f + g; printf("Result: %dn", result); return result; } }; int main() { int a = 1, b = 2, c = 3, d = 4, e = 5, f = 6, g = 7; int result = 0; Test test; result = test.func(a, b, c, d, e, f, g); printf("Result: %dn", result); return 0; }
cdecl
传递参数

开辟栈空间,保存栈环境

恢复环境,恢复栈空间,返回

平栈,保存返回值

fastcall
传递参数

开辟栈空间,保存环境

恢复环境,恢复栈空间,平栈

stdcall
传递参数

开辟栈空间,保存环境

恢复环境,恢复栈空间,平栈

thiscall
传递参数

开辟栈空间,保存环境

恢复环境,恢复栈空间,平栈

总结
| 函数调用约定 | 参数传递方式 | 平栈方式 |
|---|---|---|
| cdecl(默认) | push(从右到左) | 函数外平栈 |
| fastcall | ecx(参数1),edx(参数2),push(从右到左) | 函数内平栈 |
| stdcall | push(从右到左) | 函数内平栈 |
| thiscall | ecx(this指针),push(从右到左) | 函数内平栈 |
值得注意的是:在winapi中大多是用stdcall
x64 汇编分析(vectorcall, thiscall)
分析汇编的源码
#include <iostream> int __vectorcall test(int a, int b, int c, int d, int e, int f, int g) { int result = 0; result += a + b + c + d + e + f + g; printf("Result: %dn", result); return result; } int main() { int a = 1, b = 2, c = 3, d = 4, e = 5, f = 6, g = 7; int result = 0; result = test(a, b, c, d, e, f, g); printf("Result: %dn", result); return 0; }
vectorcall
传递参数

保存参数,开辟栈空间,(保存环境)

恢复栈空间

总结
| 函数调用约定 | 参数1 | 参数2 | 参数3 | 参数4 | 参数5以上 | 平栈方式 |
|---|---|---|---|---|---|---|
| vectorcall | rcx | rdx | r8 | r9 | 堆栈传递 | 函数内平栈 |
| vectorcall(浮点参数) | xmm0 | xmm1 | xmm2 | xmm3 | 堆栈传递 | 函数内平栈 |
| thiscall | rcx(this) | rdx | r8 | r9 | 堆栈传递 | 函数内平栈 |
我这里给出thiscall,vectorcall的浮点表现形式,thiscall,因为在底层默认函数第一个参数传递this指针,这两种函数调用约定在x64中本质是一样的