assembly - x64 调用约定中前 4 个参数的影子空间的用途是什么

标签 assembly x86-64 calling-convention

根据https://learn.microsoft.com/en-us/cpp/build/stack-usage?view=msvc-170 ,调用者必须始终为 4 个寄存器参数分配足够的空间,即使被调用者没有那么多参数。始终为寄存器参数分配空间,即使参数本身从未驻留在堆栈中。

这4个寄存器参数的影子空间有什么用?

我反汇编了一些VS和G++编译的程序,发现被调用者一开始就将寄存器参数保存在影子空间中。例如,WinMain(HINSTANCE *hInstance, HINSTANCE *hPrevInstance, char *lpCmdLine, int nCmdShow)函数在其开头执行以下推送:

mov     [rsp+arg_18], r9d
mov     [rsp+arg_10], r8
mov     [rsp+arg_8], rdx
mov     [rsp+arg_0], rcx

为什么被调用者将寄存器参数保存在影子空间中?
如果被调用者必须将寄存器参数保存在堆栈中,为什么他们使用寄存器来传递参数而不是直接通过堆栈传递所有参数?

最佳答案

被调用者可能需要也可能不需要保存它们,这完全取决于情况。例如,仅返回参数总和的函数可能不需要保存它们。在这种情况下,使用寄存器可以保存存储和加载,而在其他情况下不会产生任何开销(存储只是从调用者移动到被调用者)。

如果您与调用者中的局部变量一起分配影子空间,那么分配影子空间实际上是免费的,并且具有以下优点:如果被调用者需要保存参数,则生成的 block 将与已存在的其余参数连续在堆栈上。

关于assembly - x64 调用约定中前 4 个参数的影子空间的用途是什么,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26959372/

相关文章:

使用 pin、perf 和 valgrind 计算二进制文件执行的指令数

c++ - 将 32 位遗留代码移植到 64 位时如何处理不断变化的数据类型大小?

multithreading - 退出临界区

c++ - 这是否仍然为一种指针函数声明别名?

c++ - 为什么不使用时分配堆栈内存?

linux - 如何在 Linux 上使用系统调用等待击键中断?

c - 调用 glibc 时的 x86-64 ELF 初始堆栈布局

parsing - 有关将语法树转换为程序集的资源?

assembly - linux g++ x64 通过 FS 段寄存器访问内存

有效处理可变数量的返回参数的 Pythonic 方法