x64汇编中的"影子空间"是什么?

Igo*_*rhi 20 c++ 64-bit assembly low-level

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

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

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

我已经拆解了我的代码,但我找不到逻辑.

IIn*_*ble 24

可以使用阴影空间(有时也称为" 溢出空间"或" 家庭空间"),以便更轻松地调试x64.

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

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

调试的挑战优化的x64代码提供有关该问题的深入信息.

  • 阴影空间对于简化 var-args 函数也很有用。他们可以[将寄存器参数转储到影子空间](http://stackoverflow.com/a/36851785/224132),然后整个参数列表是一个连续的数组。IIRC,ABI 甚至要求在整数和 xmm 寄存器中传递 FP args,因此例如,`printf` 的开头可以将 4 个整数 arg regs 转储到阴影空间中,而无需弄清楚哪些 args 是 `double`。或者它可以直接使用 `xmm0` 中的副本。这是非常令人讨厌的冗余,并且似乎在性能上的简单性太过分了。:/ (3认同)
  • @eva:调试器是一个观察者.它并不意味着改变它观察到的代码.当然,调试器可以使用其私有内存来跟踪函数调用的寄存器值.但是,在程序开始运行后连接调试器时,您将无法检查完整的调用堆栈.虽然我不知道更好的解决方案,但我和你在一起,这一切都让人觉得有点笨拙. (3认同)

Eld*_*Bug 7

影子空间是必须为被调用过程保留的必需32字节(4x8字节).它只是意味着在调用之前必须在堆栈上提供32个字节.这个空间可以保持未初始化,没关系.

请注意,在x64调用约定中,第4个之后的参数被压入堆栈,这些参数位于此阴影空间的顶部(在32个字节之前推送).

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

调用x64时也应考虑堆栈对齐等问题.