首页 > 解决方案 > Microsoft Stack 是否始终与 16 字节对齐?

问题描述

Kip Irvine 的Assembly Language, Seventh Edition for x86 Processors中,第 211 页,它在5.53 The x86 Calling Convention which address the Microsoft x64 Calling Convention 下说,

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

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

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

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

标签: windowsassemblystackx86-64calling-convention

解决方案


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

是的这就是 ABI 要求/保证call.


您可以函数内执行任何操作,例如 3x 16 位推送,然后sub rsp, (24 - 3*2)在进入函数后重新获得 16 字节堆栈对齐。

或者movq xmm0, rsp,然后rsp用作额外的临时寄存器以获得 16 个整数寄存器,直到您在制作另一个callret. 1

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

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


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

写在ESP下面是否有效?相关:正如 Ped7g 指出的那样,如果某些东西可以异步使用堆栈指针下方的空间,那么如果 RSP 根本不指向堆栈内存,它可能会中断。

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


推荐阅读