Windows 什么是';阴影空间';在x64组件中?

Windows 什么是';阴影空间';在x64组件中?,windows,assembly,x86-64,calling-convention,abi,Windows,Assembly,X86 64,Calling Convention,Abi,我找到了很多关于这个阴影空间的话题,但我都找不到答案,所以我的问题是: 在进入过程之前,我需要从堆栈指针中减去多少字节 我应该在减去“阴影空间”之前将过程参数推送到堆栈中吗 我已经反汇编了代码,但找不到逻辑。阴影空间是必须为调用的过程保留的32字节(4x8字节)。这只意味着您必须在调用之前在堆栈上提供32个字节。这个空间可以不初始化,这无关紧要 注意,在x64调用约定中,第4个之后的参数被推送到堆栈上,堆栈位于该阴影空间的顶部(推送到32字节之前) 简而言之,您可以看到x64中的函数至少有4个参

我找到了很多关于这个阴影空间的话题,但我都找不到答案,所以我的问题是:

在进入过程之前,我需要从堆栈指针中减去多少字节

我应该在减去“阴影空间”之前将过程参数推送到堆栈中吗


我已经反汇编了代码,但找不到逻辑。

阴影空间是必须为调用的过程保留的32字节(4x8字节)。这只意味着您必须在调用之前在堆栈上提供32个字节。这个空间可以不初始化,这无关紧要

注意,在x64调用约定中,第4个之后的参数被推送到堆栈上,堆栈位于该阴影空间的顶部(推送到32字节之前)

简而言之,您可以看到x64中的函数至少有4个参数,但值为4个寄存器中的第一个参数


调用x64时,还应考虑堆栈对齐等问题。

阴影空间是必须为调用的过程保留的32字节(4x8字节)。这只意味着您必须在调用之前在堆栈上提供32个字节。这个空间可以不初始化,这无关紧要

注意,在x64调用约定中,第4个之后的参数被推送到堆栈上,堆栈位于该阴影空间的顶部(推送到32字节之前)

简而言之,您可以看到x64中的函数至少有4个参数,但值为4个寄存器中的第一个参数

调用x64时,还应考虑堆栈对齐等问题。

阴影空间(有时也称为溢出空间或主空间)比被调用函数拥有的返回地址高32字节(可以用作暂存空间),低于堆栈参数(如果有)。在运行
call
指令之前,调用者必须为被调用者的影子空间预留空间

它旨在使调试x64更容易

回想一下。如果闯入调试器并检查线程的调用堆栈,将无法看到传递给函数的任何参数。寄存器中存储的值是瞬态的,在向上移动调用堆栈时无法重建

这就是主空间发挥作用的地方:编译器可以使用主空间在堆栈上留下寄存器值的副本,以便以后在调试器中检查。这通常发生在未优化的构建中。但是,当启用优化时,编译器通常将主空间视为可供临时使用。堆栈上没有留下任何副本,调试崩溃转储将变成一场噩梦

提供有关该问题的深入信息。

阴影空间(有时也称为溢出空间或主空间)比被调用函数拥有的返回地址(并可用作暂存空间)高32字节,位于堆栈参数(如果有)下方。在运行
call
指令之前,调用者必须为被调用者的影子空间预留空间

它旨在使调试x64更容易

回想一下。如果闯入调试器并检查线程的调用堆栈,将无法看到传递给函数的任何参数。寄存器中存储的值是瞬态的,在向上移动调用堆栈时无法重建

这就是主空间发挥作用的地方:编译器可以使用主空间在堆栈上留下寄存器值的副本,以便以后在调试器中检查。这通常发生在未优化的构建中。但是,当启用优化时,编译器通常将主空间视为可供临时使用。堆栈上没有留下任何副本,调试崩溃转储将变成一场噩梦


提供有关此问题的深入信息。

非常感谢,因此最小保留必须为32字节,是否存在最大保留?@IgorBezverhi不在约定中,但被调用函数只需要32字节+附加参数,因此它将(应该)永远不会使用更多。对于当前函数,只要不超过最大堆栈大小(所谓的堆栈溢出),您可以使用任意数量的函数。非常感谢,因此最小保留必须为32字节,是否有最大保留?@IgorBezverhi不在约定中,但被调用函数只需要32字节+附加参数,因此它将(应该)永远不要使用更多。对于当前函数,只要不超过最大堆栈大小(所谓的堆栈溢出),就可以使用任意数量的函数。阴影空间也有助于简化var args函数。它们可以,然后整个参数列表是一个连续数组。IIRC,ABI甚至要求在整数和xmm寄存器中传递FP args,因此,例如,
printf
的开头可以将4个整数arg regs转储到阴影空间中,而不知道是哪个args是
double
。或者它可以直接使用
xmm0
中的副本。这是非常麻烦的冗余,似乎过于简单而不是性能。:/这对我来说没有意义-为什么调试器不能足够聪明地在堆栈上分配新空间(alloca)或者在堆上为寄存器值分配空间?为什么要在需要调试的事件中始终分配空间?@eva:调试器是一个观察者。它并不意味着要更改它观察到的代码。当然,调试器可以使用其私有内存跟踪函数调用中的寄存器值。但是,这样一来,您就无法进行检查在程序开始运行后,当你附加一个调试器时,调用整个堆栈。尽管我不知道有更好的解决方案,但我同意你的看法,这一切感觉有点笨拙。函数“拥有”它的堆栈参数也可以,并且可以在函数输入后修改它们。为了能够在回溯时看到实际调用的参数函数,您必须编写使用不同变量的代码,而不是修改