【发布时间】:2015-07-23 06:48:01
【问题描述】:
我找到了很多关于这个影子空间的话题,但我都找不到答案,所以我的问题是:
在进入过程之前,我需要从堆栈指针中减去多少字节?
我应该在减去“影子空间”之前将过程参数压入堆栈吗?
我已经反汇编了我的代码,但我找不到逻辑。
【问题讨论】:
标签: windows assembly x86-64 calling-convention abi
我找到了很多关于这个影子空间的话题,但我都找不到答案,所以我的问题是:
在进入过程之前,我需要从堆栈指针中减去多少字节?
我应该在减去“影子空间”之前将过程参数压入堆栈吗?
我已经反汇编了我的代码,但我找不到逻辑。
【问题讨论】:
标签: windows assembly x86-64 calling-convention abi
影子空间是必须为调用过程保留的 32 字节(4x8 字节)。这只是意味着您必须在调用之前在堆栈上提供 32 个字节。这个空间可以不初始化,没关系。
请注意,在 x64 调用约定中,第 4 个之后的参数被压入堆栈,这些参数位于此影子空间的顶部(在 32 个字节之前被压入)。
简而言之,您可以将其视为 x64 中的函数至少有 4 个参数,但 4 的值在寄存器中位于首位。
调用 x64 时还应考虑堆栈对齐等问题。
【讨论】:
影子空间(有时也称为溢出空间或主空间)比被调用函数拥有的返回地址高32个字节(并且可以用作暂存空间),如果有的话,在堆栈参数下方。在运行call 指令之前,调用者必须为其被调用者的影子空间保留空间。
它的目的是让调试 x64 更容易。
回想一下first 4 parameters are passed in registers。如果您闯入调试器并检查线程的调用堆栈,您将无法看到传递给函数的任何参数。存储在寄存器中的值是瞬态的,在调用堆栈向上移动时无法重建。
这就是 Home 空间 发挥作用的地方:编译器可以使用它在堆栈上保留寄存器值的副本,以便稍后在调试器中检查。这通常发生在未优化的构建中。然而,当启用优化时,编译器通常将 Home 空间 视为可供临时使用。堆栈上没有任何副本,调试故障转储会变成一场噩梦。
Challenges of Debugging Optimized x64 Code 提供有关该问题的深入信息。
【讨论】:
printf 的开头可以将 4 个整数 arg regs 转储到影子空间中,而无需弄清楚哪些 args 是 double。或者它可以直接使用xmm0 中的副本。这是非常烦人的多余,而且似乎过于简单而不是性能。 ://