EFLAGS 状态

State of EFLAGS

在过去的几天里,我一直在努力解决试图获取 EFLAGS 状态的奇怪行为。为此,我编写了以下代码:

#include <stdio.h>

int flags_state()
{

  int flags = 0;

  __asm__ __volatile__("pushfq");
  __asm__ __volatile__("pop %%rax": "=a"(flags));

  return flags;
}

int main()
{

  printf("Returning EFLAGS state: 0x%x\n", flags_state());
  return 0;

}

当它运行时,我得到:

./flags
Returning EFLAGS state: 0x246

当我打印两次标志时,它变得越来越奇怪

Returning EFLAGS state: 0x246
Returning EFLAGS state: 0x206

当我尝试打印 6 次时,它发生了变化

Returning EFLAGS state: 0x246
Returning EFLAGS state: 0x202
Returning EFLAGS state: 0x202
Returning EFLAGS state: 0x202
Returning EFLAGS state: 0x202
Returning EFLAGS state: 0x202

最后是最奇怪的(至少对我来说)当我打印 8 次时

Returning EFLAGS state: 0x246
Returning EFLAGS state: 0x206
Returning EFLAGS state: 0x206
Returning EFLAGS state: 0x206
Returning EFLAGS state: 0x206
Returning EFLAGS state: 0x206
Returning EFLAGS state: 0x206
Returning EFLAGS state: 0x206

那么,为什么我第一时间得到的是0x246呢?根据英特尔手册,不应该是 0x2 吗?为什么当我尝试打印更多次并继续更改时它会发生变化?

  __asm__ __volatile__("pushfq");
  __asm__ __volatile__("pop %%rax": "=a"(flags));

您不能像这样分解 asm 语句之间的指令。当 asm 语句移动堆栈指针而不将其放回原处时,编译器会感到非常困惑。它可能单独看起来没问题,但想象一下内联函数;编译器可以决定针对明显不相关的代码移动 asm。

另一个问题是,由于 ,编译器可能已将重要数据 放在堆栈指针 下方:就在您的 pushfq 会覆盖的位置它。

这不是那么容易解决的。我最好的猜测是

unsigned long get_rflags(void) {
    unsigned long result;
    asm("sub 8, %%rsp ; pushfq ; pop %0 ; add 8, %%rsp" 
        : "=r"  (result) : : "cc");
    return result;
}

或者将其编写为纯粹在 asm 中的“裸”函数,以便您知道编译器不参与。

(如 所述,可以通过编写 add $-128, %%rsp ... sub $-128, %%rsp 进行较小的代码大小优化,因为 -128 适合符号扩展的 8 位,但 +128 没有。)

( sub/add 本身会影响算术标志,如下所述,但它们又一次被频繁更改,以至于很难赋予它们的值太多意义。我想你可以使用 lea -128(%%rsp), %%rsp 如果你真的关心。)


至于变化的值,您会看到第 2 位和第 6 位的变化:奇偶校验标志和零标志。由于几乎每个算术指令都根据结果设置这些,并且其他代码在您的调用之间执行(例如 printf 的所有代码!),我们会看到这些值发生变化也就不足为奇了。进位、符号、溢出和辅助进位标志同样是“易变的”。这没什么奇怪的。

没有理由期望值 0x2:各种代码都已 运行,几乎所有代码都会影响标志,那么为什么所有其他标志都需要清除?

如果愿意,您可以在调试器中逐条指令单步执行代码,并观察 RFLAGS 的变化。您可能会看到它在一个 printf 和下一个之间更改了数百次。

So, why did I get 0x246 at the first time? Shouldn't be 0x2 according Intel's manual?

flags_state() 第一次调用之前,一些代码在系统中执行,结果大多数标志状态是随机的,你不能在通用标志上假设任何值,比如 ZF (0x40) 它可以是和设置并重置..以及如何将 Intel 的手册? 与此处关联起来?

Why did it change when I try to print it more times and continue change?

函数不得保留 ZF 标志(与 windows 中的 DF 不同 - 在 return 中必须为 0) - 因此此标志在函数后具有哪个值return - 也未定义 - 如果只有你自己不在 asm 上编写所有代码并完全控制它。事实上 ZFflags_state return 之后被重置并且在 flags_state 的序言中没有改变 - 结果是第一次 - 你有在前面的代码中设置的值然后已经全部时间相同的值,在 flags_state 中设置(你错了,它 继续改变 - 它没有改变,如你的输出所示 - 0x206 一直)