汇编语言Microsoft x64调用规范简介
Microsoft 遵循固定模式实现 64 位编程中的参数传递和子程序调用,该模式被称为 Microsoft x64 调用规范(Microsoft x64 calling convention)。它既用于 C 和 C++ 编译器,也用于 Windows API库。
只有在要么调用 Windows 函数,要么调用 C 和 C++ 函数时,才需要使用这个调用规范。它的特点和要求如下所示:
1) 由于地址长为 64 位,因此 CALL 指令把 RSP(堆栈指针)寄存器的值减去8。
2) 第一批传递给子程序的四个参数依次存放于寄存器 RCX、RDX、R8 和 R9。因此,如果只传递一个参数,它就会被放入 RCX。如果还有第二个参数,它就会被放入 RDX,以此类推。其他参数按照从左到右的顺序入栈。
3) 长度不足 64 位的参数不进行零扩展,因此,其高位的值是不确定的。
4) 如果返回值的长度小于或等于 64 位,那么它必须放在 RAX 寄存器中。
5) 主调者要负责在堆栈中分配至少 32 字节的影子空间,以便被调用的子程序可以选择将寄存器保存在这个区域中。
6) 调用子程序时,堆栈指针(RSP)必须对齐 16 字节边界。CALL 指令将 8 字节的返回地址压入堆栈,因此,主调程序除了把堆栈指针减去 32 以便存放寄存器参数之外,还要减去8。
7) 被调用子程序执行结束后,主调程序需负责从运行时堆栈中移除所有的参数和影子空间。
8) 大于 64 位的返回值存放于运行时堆栈,由 RCX 指出其位置。
9) 寄存器 RAX、RCX、RDX、R8、R9、R10 和 R11 常常被子程序修改,因此,如果主调程序想要保存它们的值,就应在调用子程序之前将它们入栈,之后再从堆栈弹出。
10) 寄存器 RBX、RBP、RDI、RSI、R12、R13、R14 和 R15 的值必须由子程序保存。
只有在要么调用 Windows 函数,要么调用 C 和 C++ 函数时,才需要使用这个调用规范。它的特点和要求如下所示:
1) 由于地址长为 64 位,因此 CALL 指令把 RSP(堆栈指针)寄存器的值减去8。
2) 第一批传递给子程序的四个参数依次存放于寄存器 RCX、RDX、R8 和 R9。因此,如果只传递一个参数,它就会被放入 RCX。如果还有第二个参数,它就会被放入 RDX,以此类推。其他参数按照从左到右的顺序入栈。
3) 长度不足 64 位的参数不进行零扩展,因此,其高位的值是不确定的。
4) 如果返回值的长度小于或等于 64 位,那么它必须放在 RAX 寄存器中。
5) 主调者要负责在堆栈中分配至少 32 字节的影子空间,以便被调用的子程序可以选择将寄存器保存在这个区域中。
6) 调用子程序时,堆栈指针(RSP)必须对齐 16 字节边界。CALL 指令将 8 字节的返回地址压入堆栈,因此,主调程序除了把堆栈指针减去 32 以便存放寄存器参数之外,还要减去8。
7) 被调用子程序执行结束后,主调程序需负责从运行时堆栈中移除所有的参数和影子空间。
8) 大于 64 位的返回值存放于运行时堆栈,由 RCX 指出其位置。
9) 寄存器 RAX、RCX、RDX、R8、R9、R10 和 R11 常常被子程序修改,因此,如果主调程序想要保存它们的值,就应在调用子程序之前将它们入栈,之后再从堆栈弹出。
10) 寄存器 RBX、RBP、RDI、RSI、R12、R13、R14 和 R15 的值必须由子程序保存。