SysV调用约定

SysV调用约定

SysV调用约定 #

参数传递 #

在调用函数时,对于整型和指针类型的实参,需要分别使用寄存器 rdi、rsi、 rdx、rcx、r8、r9,按函数定义时参数从左到右的顺序进行传值。而若一个函数接收的参数超过了 6 个,则余下参数将通过栈内存进行传送。此时,多出来的参数将按照从右往左(RTL)的顺序被逐个压入栈中。

对于浮点参数,编译器将会使用另外的 xmm0 到 xmm7,共 8 个寄存器进行存储。对于更宽的值,也可能会使用 ymm 与 zmm 寄存器来替代 xmm 寄存器。而 xmm、 ymm、zmm 寄存器,都是由 x86 指令集架构中名为 AVX(Advanced Vector Extensions)的扩展指令集使用的。这些指令集一般专门用于浮点数计算以及 SIMD 相关的处理过程。

返回值传递 #

当函数调用产生整数类型的返回值,且小于等于 64 位时,通过寄存器 rax 进行传递;当大于 64 位,小于等于 128 位时,则使用寄存器 rax 与 rdx 分别存储返回值的低 64 位与高 64 位。对于复合类型(比如结构体)的返回值,编译器可能会直接使用栈内存进行“中转”。对于浮点数类型的返回值,同参数传递类似,编译器会默认使用 xmm0 与 xmm1 寄存器进行存储。而当返回值过大时,则会选择性使用 ymm 与 zmm 来替代 xmm 寄存器。

寄存器使用 #

对于寄存器 rbx、rbp、rsp,以及 r12 到 r15,若被调用函数需要使用它们,则需要该函数在使用之前将这些寄存器中的值进行暂存,并在函数退出之前恢复它们的值(callee-saved)。而对于其他寄存器,则根据调用方的需要,自行保存和恢复它们的值(caller-saved)。

堆栈清理 #

每一个函数在调用结束前,都需要由它自身完成堆栈的清理工作。

其他约定 #

除此之外,SysV 调用约定还有下面这几点规定:

  1. 函数在被 call 指令调用前,需要保证栈顶于 16 字节对齐,也就是栈顶的所在地址值(以字节为单位)是 16 的倍数;
  2. 从栈顶向上保留 128 字节作为 “Red Zone”;
  3. 不同于用户函数的调用过程,系统调用(System Call)函数需使用寄存器 rdi、rsi、rdx、r10、r8、r9 传递参数。

Red Zone 是位于栈顶向上(低地址方向)的一段固定长度的内存段,这块区域通常可以被函数调用栈中的“叶子”函数(即不再调用其他函数的函数)使用。这样,在需要额外的栈内存时,就能在一定条件下省去先调整栈内存大小的过程。

Viewpoint #

From #

05|代码封装(上):函数是如何被调用的?