【问题标题】:What is the 'shadow space' in x64 assembly?x64 程序集中的“阴影空间”是什么?
【发布时间】:2015-07-23 06:48:01
【问题描述】:

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

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

我应该在减去“影子空间”之前将过程参数压入堆栈吗?

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

【问题讨论】:

    标签: windows assembly x86-64 calling-convention abi


    【解决方案1】:

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

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

    简而言之,您可以将其视为 x64 中的函数至少有 4 个参数,但 4 的值在寄存器中位于首位。

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

    【讨论】:

    • 非常感谢,所以最小预留必须是 32Bytes,有最大预留吗?
    • @IgorBezverhi 不在约定中,但被调用函数只需要 32 个字节 + 附加参数,因此它不会(应该)使用更多。对于当前函数,你想用多少就用多少,只要不超过最大栈大小(所谓栈溢出)即可。
    【解决方案2】:

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

    它的目的是让调试 x64 更容易。

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

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

    Challenges of Debugging Optimized x64 Code 提供有关该问题的深入信息。

    【讨论】:

    • 阴影空间对于简化 var-args 函数也很有用。他们可以只是dump the register args into the shadow space,然后整个参数列表就是一个连续的数组。 IIRC,ABI 甚至要求 FP args 在整数和 xmm 寄存器中传递,例如printf 的开头可以将 4 个整数 arg regs 转储到影子空间中,而无需弄清楚哪些 args 是 double。或者它可以直接使用xmm0 中的副本。这是非常烦人的多余,而且似乎过于简单而不是性能。 ://
    • 这对我来说没有意义 - 为什么调试器不能足够聪明地在堆栈(alloca)或堆上为寄存器值分配新空间?为什么要在要调试的事件中始终分配空间?
    • @eva:调试器就是观察者。这并不是要更改它观察到的代码。当然,调试器可以使用其私有内存来跟踪函数调用中的寄存器值。但是,当您在程序开始运行后附加调试器时,您将无法检查完整的调用堆栈。尽管我不知道更好的解决方案,但我同意你的看法,这一切都感觉有点笨拙。
    • 一个函数也“拥有”它的堆栈参数,并且可以在函数进入后修改它们。为了能够看到 args 函数在回溯时实际被调用,您必须编写使用不同变量的代码,而不是修改传入的 args。 (或者,如果您这样做,编译器可以复制堆栈 args。)由于调试信息显示在哪里可以找到所有 var,而不仅仅是 args,因此您可以在堆栈帧中看到 arg 变量,无论是否存在阴影空间,编译器都会在其中溢出它们。例如x86-64 System V 调用约定对此没有问题,即使没有阴影空间。
    • @pet:我相信我承认在我的回答中(“启用优化后,[...] 编译器通常将 Home 空间视为可用于划痕使用。”)。无法保证溢出到主空间的参数将在函数调用中继续存在,但还是有希望的。使用 rcx、rdx、r8 和 r9 几乎是给定的,它们将在下一次函数调用时被覆盖。如果不是为了调试,家庭空间的主要目标是什么?可变参数/非原型函数真的是唯一的驱动力吗?
    猜你喜欢
    • 2010-10-14
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 2017-08-27
    • 1970-01-01
    相关资源
    最近更新 更多