为了能够调用函数,需要有一种公认的方式来传递参数。 如果程序是完全独立的二进制文件,编译器可以自由决定调用约定。 然而在现实中,会使用共享库(例如 libc)以便公共代码可以只存储一次并动态链接到需要它的程序,从而减少程序大小。 在 Linux 二进制文件中,实际上只有两种常用的调用约定:cdecl 用于 32 位,SysV 用于 64 位。
cdecl
在 Linux 上的 32 位二进制文件中,函数参数以相反的顺序传入堆栈。 例如以下函数:
int add(int a, int b, int c) {
return a + b + c;
}
在被调用时,参数会按c
、b
、a
的顺序被压入栈。
SysV
对于64位二进制文件,函数参数先按顺序在一些寄存器中传递:
- RDI
- RSI
- RDX
- RCX
- R8
- R9
然后,剩下的参数会以逆序压入堆栈中,就像 cdecl 中一样。
其他约定
只要编译器知道这个惯例是什么,任何传递参数的方法都可以使用。因此,过去有许多调用惯例,现在已经不经常使用了。参见 Wikipedia 的列表。