使用“pushf”并弹出到 32 位寄存器是否会破坏堆栈?

max*_*dev 2 c assembly kernel osdev

我想通过将 CPU 标志压入堆栈来读取它们,然后将它们弹出到寄存器中,如下所示:

uint32_t getEflags() {
    uint32_t eflags;
    asm ("pushf");
    asm ("pop %eax");
    asm ("mov %%eax, %0" : "=r"(eflags));
    return eflags;
}
Run Code Online (Sandbox Code Playgroud)

现在我发现这是一个错误,因为Pushf只压入 EFLAGS 的低 16 位,然后我将它们弹出到 32 位寄存器中(我需要Pushfd)。

这意味着我弹出的内容基本上比最初推送的要多 - 返回这里时我的堆栈会发生什么情况?

Jes*_*ter 5

指令集参考是这样说的:

将堆栈指针减 4(如果当前操作数大小属性为 32)并将 EFLAGS 寄存器的全部内容压入堆栈

它还说:

PUSHF(推送标志)和 PUSHFD(双推送标志)助记符引用相同的操作码。PUSHF 指令适用于操作数大小属性为 16 时,PUSHFD 指令适用于操作数大小属性为 32 时。某些汇编器可能会在使用 PUSHF 时强制操作数大小为 16,在使用 PUSHFD 时强制操作数大小为 32。其他人可能将这些助记符视为同义词(PUSHF/PUSHFD),并使用操作数大小属性的当前设置来确定要从堆栈推送的值的大小,而不管使用的助记符如何。

所以没有问题。不过,您的汇编已损坏,它应该更像是这样的:

__asm__ __volatile__(
    "pushf\n\t"
    "pop %0"
: "=rm" (eflags));
Run Code Online (Sandbox Code Playgroud)

  • 一方面,OP 的代码忘记告诉编译器“eax”已被破坏,因此它可能会破坏周围的代码。此外,编译器还可以对单独的 asm 块重新排序。此外,作为一个优化点,输出操作数可以是任何寄存器或内存,不需要经过“eax”。 (4认同)