目录
X64调用约定利用寄存器来传递部分函数参数,不规范的函数调用规则逐渐减少。现主要有两种比较通用的函数调用规则。一种是Microsoft X64函数调用约定,参见以下链接:MSVC平台下X64处理器函数调用规则——底层机制_ComputerInBook的博客-优快云博客目录1. 默认的调用规则2. 内存边界对齐问题3. 解开性(Unwindability)4. 参数传递5. 可变数量参数的传递6. 非原型函数7. 函数返回值8. 调用函数或被调函数存储寄存器9. 函数指针10. 对较旧代码的浮点支持11. 浮点状态和控制寄存器(Float Point Status and Control Register,简记为FPSCR)12. 多媒体扩展控制和状态寄存器(Multimedia Extensions Control andhttps://blog.youkuaiyun.com/ComputerInBook/article/details/124342574
另一种是System V AMD64 函数调用约定(即Application Binary Interface,简记为ABI)。这里主要讲System V AMD64约定的规则。
遵循System V AMD64 ABI约定的操作系统平台主要有:Solaris, Linux, FreeBSD, macOS,等等。是很多Unix和类Unix操作系统事实上的ABI标准。OpenVMS的X64 ABI调用约定基于System V ABI调用标准,并做了一些后向兼容的扩展。
1. System V AMD64 ABI约定
前6个整数参数(整型参数或指针参数)使用RDI, RSI, RDX, RCX, R8, R9这6个寄存器传递(在嵌套函数的情况下,R10 用作静态链指针);XMM0, XMM1, XMM2, XMM3, XMM4, XMM5, XMM6 和 XMM7用于传递前8个浮点参数。超出规定寄存器个数的参数采用内存栈传递。整数返回值最多64位,存储在RAX中以返回;如果返回值达到128位,存储在RAX和RDX中以返回。类似地,64位浮点值放在寄存器XMM0以返回,128位浮点值放在寄存器XMM0和XMM1以返回。如果存在更宽位的值,使用YMM寄存器和ZMM寄存器传递参数及返回值。用于传递参数的寄存器如下图:
参数类型 | 寄存器 |
整数参数 1-6 | RDI, RSI, RDX, RCX, R8, R9 |
浮点参数 1-8 | XMM0 - XMM7 |
超出部分的参数 | 内存栈 |
静态链指针 | R10 |
如果被调函数期望使用寄存器RBX, RSP, RBP, and R12–R15(称为易失性寄存器),则在返回被调函数之前,必须恢复它们在使用之前的值;如果调用函数期望恢复所有其它寄存器调用前的值,则在使用之前必须先保存这些寄存器的值。
对于叶函数(leaf-node function,即,没有进一步函数其它调用的函数),在栈指针之下保留了128字节的空间。这段空间称为红色区域(red zone)。这块区域不能被任何信号或中断处理器占用。因此,编译器可以使用这块区域来保存局部变量。编译器通过使用这块区域来忽略函数开头的一些指令(调整寄存器RSP,RBP)。然而,其它函数可以占用这块区域,因此,这块区域应当仅用于叶函数。gcc和clang使用-mno-red-zone标识来禁止对红色区域的优化。
对于被调函数是可变参数函数(variadic function)的情况,以向量寄存器传递浮点参数的数量必须由调用函数在寄存 器AL寄存器中指定。
与Microsoft调用不同的是,系统并没有提供影子内存(shadow space),而是,在函数入口点,返回地址在栈上紧邻第7个整型函数参数。
栈空间采用16字节边界对齐方式。在栈之下保留有128字节的红色区域。内核接口使用RDI, RSI, RDX, R10, R8 和R9寄存器。在C++类函数中,this是第一个参数。
2. System V AMD64 ABI的栈帧结构
除了寄存器之外,每个函数在运时栈上都拥有一个函数栈帧。这个栈帧的方向为从高地址向低地址向下递减。下图为栈帧结构:
位置 | 内容 | 栈帧 |
8n+16(%rbp) | 内存参数8字节n ... 内存参数8字节0 | 高地址 |
8(%rbp) 0(%rbp) -8(%rbp) 0(%rbp) -128(%rbp) | 返回地址 | 当前地址 |
前一个%rbp值 | ||
未指定 ... 变量大小 | ||
红色区域 |
在输入参数和末尾,将按16字节边界对齐(如果使用在栈上传递__m256大小的整数,则采用32字节的边界对齐方式)。换句话说,当控制权交给函数入口头的时候,(%rsp+8)的值总是16(32)的倍数。栈指针%rsp总是指向最新分配的栈的底端。%rsp指向的位置以外的 128 字节区域被认为是保留的,不应被信号或中断处理程序修改。因此,函数可以使用此区域存储局部变量。特别是,叶函数使用这个区域作为其整个栈帧,而不是作为序言和结语的代码去调整栈桢。