【问题标题】:Is the Microsoft Stack always aligned to 16-bytes?Microsoft Stack 是否始终与 16 字节对齐?
【发布时间】:2019-03-07 23:41:46
【问题描述】:

Assembly Language, Seventh Edition for x86 Processors by Kip Irvine 的第 211 页,它在 5.53 The x86 Calling Convention 下说,它解决了 Microsoft x64 调用约定,

  1. 调用子程序时,堆栈指针 (RSP) 必须在 16 字节边界(16 的倍数)上对齐。 CALL 指令将一个 8 字节的返回地址压入堆栈,因此调用程序必须从堆栈指针中减去 8,除了它已经为影子空间减去的 32。李>

接着在sub rsp, 20h 之前显示一些带有sub rsp, 8 的程序集(对于32 字节的影子空间)。

这是一个安全的约定吗? Microsoft 堆栈是否保证在CALL 指令之前 对齐 16 字节?或者,这本书假设堆栈是错误的

  1. CALL 之前的 16 字节对齐
  2. 有一个 8 字节的返回地址使用 CALL 压入堆栈
  3. 需要额外的sub rsp, 8; 才能恢复到 16 字节对齐?

【问题讨论】:

  • 是的,堆栈指针(Rsp)在调用之前必须对齐 16 字节(任何外部 api,用于自身代码(在 asm 中,因为 c/c++ 不是让你这样做,你当然违反了这个))。这只是协议,因为任何 api 都等待并基于此。当然,您并不完全需要sub rsp, 8。说可能sub rsp, 78h 和许多其他人
  • 您将编写/执行代码的要求与机器功能混合在一起。代码需要在调用之前将堆栈对齐到 16 个字节,但是机器代码可以通过执行一些不符合要求的操作来轻松打破该规则,例如将字 (2B = 16b) 值推入堆栈。这样的代码很可能会触发一些错误,当被调用的代码会尝试利用该对齐假设来发挥自己的优势并且rsp 将是错误的。但是 CPU 本身不会阻止您调用错误对齐的 rsp 的其他代码。
  • @Ped7g 不,我问的是满足 x64 ABI 的要求。每次调用后通过将堆栈增长 8 字节以实现 16 字节对齐来盲目调整堆栈是否安全。
  • sub rsp,8 是安全的,因为可以安全地假设调用您的代码按要求运行(即安全,因为代码破坏它是错误,应该修复)。如果上面的代码不能满足这个要求,那么sub rsp,8 也将无法重新对齐rsp,并且调用 next 函数可能会因此而失败。您可能需要一些时间才能真正找到有效使用该对齐方式的函数(例如,用于对齐的矢量化内存访问),因此您可能经常通过调用未对齐的rsp 函数来逃避,但这只是错误,没有演示一下,这不是正确的代码。
  • 相关:Why does the x86-64 / AMD64 System V ABI mandate a 16 byte stack alignment? - 同样的设计原因也适用于 MS。

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


【解决方案1】:

我问的是满足 x64 ABI 的要求。每次调用后通过将堆栈增长 8 字节以实现 16 字节对齐来盲目调整堆栈是否安全?

是的,这就是 ABI 要求/保证 16 字节对齐的全部意义call


您可以函数中做任何你想做的事情,例如 3x 16 位推送,然后 sub rsp, (24 - 3*2) 在进入函数后重新获得 16 字节堆栈对齐。

或者movq xmm0, rsp,然后使用rsp作为一个额外的临时寄存器来获得16个整数寄存器,直到你在创建另一个callret之前恢复它。1

不要求 RSP 在每个指令之后对齐 16 字节,仅在函数调用边界处。这就是它们被称为“调用约定”的原因,不是“编码标准”。

这与保留调用的 rbx 类似。如果您将它保存/恢复到堆栈中、xmm0 中、静态存储中、是否将其取反然后再次取反,或者您根本不触摸它,都没有关系。重要的是,当您返回调用者时,它的值与调用函数时的值相同。


脚注 1:只要您没有任何可能在用户空间堆栈上运行的异步回调/SEH 处理程序,就可以工作。这并不能真正保证安全,但可能会像黑客一样工作。

Is it valid to write below ESP? 相关:正如 Ped7g 指出的那样,如果某些东西可以异步使用堆栈指针下方的空间,那么如果 RSP 根本不指向堆栈内存,它可能会中断。

我见过一个 32 位示例 avisynth 视频过滤器(我认为),它使用它来获得 8 个 tmp regs(当没有 MMX 可用时),在使用这个技巧之前首先在代码中调试大警告 cmets。

【讨论】:

  • 最近有一个关于在 Windows 中访问 rsp-8 的问题,我看到的结论是,它在形式上是不安全的(MS 文档中只有非常小的提及暗示 @987654329 下的区域@ 是易变的),所以你不应该在 windows 下使用 rsp 作为 GPR。尽管从技术上讲,当前操作系统的实现似乎是安全的......等等,你是我认为的答案之一,所以你应该知道,嗯......然后我对那个例子有点困惑(这也是 IMO无论如何,对于不遵循您的想法的人来说,从 movq xmm0,rsp 开始就很难看到)。
  • @Ped7g:我见过一个使用 ESP 作为临时的 32 位视频过滤器的例子。添加了更多关于潜在不安全性的脚注。是的,如果有任何东西会破坏堆栈下方的空间,那么如果 RSP 根本不是堆栈空间,它就会中断。
  • 我曾经尝试过使用 ESP 作为 GPR 来(据称)优化一些代码,但它并没有提高速度。可能的结论:适合 L1 缓存比使用另一个寄存器更重要。
  • @zx485:这是一个非常小众的用例。但是如果你是uop吞吐量的瓶颈,你可以通过这种方式摆脱一些溢出/重新加载,这可能是一个加速。我认为我记得看到的案例是没有 MMX 的像素处理或音频 DSP 循环,因此 regs 非常宝贵。不适合大规模展开的 L1i 缓存是肯定的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多