我有一个适用于 Linux x64 的汇编应用程序,我通过寄存器将参数传递给函数,因此我使用了某种调用约定,在本例中为
fastcall
。现在我想从汇编应用程序调用一个 C 函数,比如说,需要 10 个参数。
我是否必须切换到
cdecl
并通过堆栈传递参数,而不管我的应用程序中的其他地方是否通过寄存器传递它们?是否允许在一个应用程序中混合调用约定?
我假设 fastcall 是指 SysV ABI 使用的 amd64 调用约定(即 Linux 使用的),其中前几个参数在
rdi
、rsi
和 rdx
中传递。
ABI稍微复杂,以下是简化的。您可能需要阅读规范了解详细信息。
一般来说,前几个(最左边的)整数或指针参数被放入寄存器
rdi
、rsi
、rdx
、rcx
、r8
和r9
中。浮点参数通过 xmm0
传递到 xmm7
。如果寄存器空间耗尽,则附加参数将从右到左通过堆栈传递。例如,要调用具有 10 个整数参数的函数:
foo(a, b, c, d, e, f, g, h, i, k);
你需要这样的代码:
mov $a,%edi
mov $b,%esi
mov $c,%edx
mov $d,%ecx
mov $e,%r8d
mov $f,%r9d
push $k
push $i
push $h
push $g
call foo
add $32,%rsp
举个具体的例子,
getnameinfo
:
int getnameinfo(
const struct sockaddr *sa,
socklen_t salen,
char *host,
size_t hostlen,
char *serv,
size_t servlen,
int flags);
您将在
sa
中传递 rdi
,在 salen
中传递 rsi
,在 host
中传递 rdx
,在 hostlen
中传递 rcx
,在 serv
中传递 r8
, servlen
中 r9
和 flags
在堆栈上。
当然可以。调用约定基于每个函数应用。这是一个完全有效的应用程序:
int __stdcall func1()
{
return(1);
}
int __fastcall func2()
{
return(2);
}
int __cdecl main(void)
{
func1();
func2();
return(0);
}
可以,但没必要。
__attribute__((fastcall))
仅要求将前两个参数传递到寄存器中 - 其他所有参数都会自动在堆栈上传递,就像 cdecl
一样。这样做是为了不通过选择特定的调用约定来限制可以赋予函数的参数数量。
在您的示例中,使用
fastcall
调用约定调用的函数有 10 个参数,前两个参数将在寄存器中传递,其余 8 个参数自动在堆栈中传递,就像标准调用约定一样。
由于您已选择将
fastcall
用于所有其他功能,我看不出您为何要针对某个特定功能更改此设置。