不同ABI调用约定下函数参数的位置

最近查看一份MINGW生成的汇编代码的时候发现其中的函数参数所在的寄存器和CSAPP上所描述的有所不同。 查阅资料之后发现,现今流行的x86_64指令集上其实有两套不同的二进制接口,进而有两套传参的顺序。 两套系统分别是 Microsoft ABI 和 System V ABI,前者用在 Windows 系统上,后者主要用在 GNU/Linux 系统上。 正是 ABI 不同导致了传参顺序的差异。 此篇文章主要用来总结这些差异。 许多编译器可以通过为函数指明调用方式来手动修改调用约定,这不在本文的考虑范围之内。

参数传递的寄存器使用顺序

  Microsoft ABI System V ABI
浮点数 xmm0-3, 栈 xmm0-7, 栈
整数和指针 rcx, rdx, r8, r9, 栈 rdi, rsi, rdx, rcx, r8, r9, 栈

对于聚合数据类型的处理比较复杂。 在 Microsoft ABI 下,如果其大小为8,16,32或64位,则按整数处理;否则,按指针处理。 在 System V ABI 下,如果其大小大于32字节,则总是在栈上; 否则,如果其是具有非平凡构造或析构函数的C++类型,则按指针传递; 否则,如果其大小大于1字节,则每个元素单独考虑(详见手册)。

Microsoft ABI 不使用 x87 寄存器,而 System V ABI 可能使用 x87 寄存器,主要用于复数计算。

易变寄存器

易变寄存器是被调用的函数可以修改的寄存器,而非易变寄存器则是函数调用前后保持不变的寄存器。 被调用的函数可以修改非易变寄存器,但必须保证返回后寄存器维持原样。 这通常是通过将寄存器的值压入栈中实现的。

  易变寄存器 非易变寄存器
Microsoft ABI rax, rcx, rdx, r8-11, xmm0-5 rbx, rbp, rdi, rsi, rsp, r12-15, xmm6-15
System V ABI 其余所有 rbx, rsp, rbp, r12-15, x87控制字

返回值

  Microsoft ABI System V ABI
浮点数 xmm0 xmm0, xmm1
整数和指针 rax rax, rdx

聚合类型的判定较为复杂,详情参见各自的手册。

附注及参考文件

更新时间: