仅仅是测试__cdecl
调用约定。
这是一个cmake项目,只有一个源文件:
#include <stdio.h>
#define CALL_CONVENTION __cdecl
void CALL_CONVENTION f(int a, int b)
{
printf("%d, %d", a, b);
}
int main()
{
f(1, 2);
return 0;
}
我正在使用set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /FA")
输出汇编代码。
当我使用cmake -G "Visual Studio 15"
进行构建时,它正在构建32位应用程序,并且所有事情都在预期之中:
...
; Line 12
push ebp
mov ebp, esp
; Line 13
push 2 ; <------- argument 2
push 1 ; <------- argument 1
call _f ; <------- call function
add esp, 8
; Line 15
xor eax, eax
; Line 16
cmp ebp, esp
call __RTC_CheckEsp
pop ebp
ret 0
_main ENDP
...
您可以看到参数由push 2
和push 1
指令传递,这是__cdecl
的调用约定。
但是,如果我使用cmake -G "Visual Studio 15 Win64"
来构建64位应用程序,则__cdecl
注释似乎不起作用(参数未通过堆栈传递):
...
; Line 12
$LN3:
push rdi
sub rsp, 32 ; 00000020H
mov rdi, rsp
mov ecx, 8
mov eax, -858993460 ; ccccccccH
rep stosd
; Line 13
mov edx, 2 ; <------ argument 2
mov ecx, 1 ; <------ argument 1
call f ; <------ call function
; Line 15
xor eax, eax
; Line 16
add rsp, 32 ; 00000020H
pop rdi
ret 0
main ENDP
...
参数由寄存器edx
和ecx
传递,而不是由堆栈传递。
那么,即使我指定了__cdecl
,为什么参数也不在x64中通过堆栈传递?如果我想在x64环境中执行相同的操作,该怎么办。
答案 0 :(得分:3)
x64具有自己的调用约定。
在ARM和x64处理器上,__cdecl被接受,但通常被编译器忽略。根据ARM和x64的约定,参数在可能的情况下在寄存器中传递,而后续参数在堆栈上传递。在x64代码中,使用__cdecl覆盖/ Gv编译器选项,并使用默认的x64调用约定。
Microsoft docs x64 calling convention
默认情况下,x64应用程序二进制接口(ABI)使用四寄存器快速调用调用约定。在调用堆栈上分配了空间作为影子存储,供被调用者保存这些寄存器。函数调用的参数与用于这些参数的寄存器之间存在严格的一对一对应关系。任何不适合8个字节,或者不是1、2、4或8个字节的参数都必须通过引用传递。
...
整数参数在寄存器RCX,RDX,R8和R9中传递
对于int a
和int b
,您可以使用ECX和EDX看到它(它们是32位,而完整的RCX和RDX是64位)。
__stdcall
,__fastcall
和__thiscall
也将被忽略。 __vectorcall
是可用的(/ Gv开关将其设置为默认值),并且是另一种调用寄存器的约定,但是与x64默认值相比,它可以在更多情况下使用寄存器,并且还有一些其他规则差异。