简体   繁体   English

EFLAGS 的 State

[英]State of EFLAGS

Over the past few days I've been struggling with a weird behaviour trying to get the states of EFLAGS.在过去的几天里,我一直在努力解决试图获得 EFLAGS 状态的奇怪行为。 To accomplish this I've written this code:为此,我编写了以下代码:

#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;

}

When it runs, I got:当它运行时,我得到:

./flags
Returning EFLAGS state: 0x246

It's getting weirder when I print out the flags twice当我两次打印标志时,它变得越来越奇怪

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

It changed when I tried to print it out 6 times当我尝试打印 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

And finally the weirdest (at least for me) when I print it out 8 times最后是最奇怪的(至少对我而言),我打印了 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

So, why did I get 0x246 at the first time?那么,为什么我第一次得到 0x246 呢? Shouldn't be 0x2 according Intel's manual?根据英特尔的手册,不应该是 0x2 吗? Why did it change when I try to print it more times and continue change?为什么当我尝试打印更多次并继续更改时它会发生变化?

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

You must not break up the instructions between asm statements like this.您不能像这样分解 asm 语句之间的指令。 The compiler will get very confused when an asm statement moves the stack pointer without putting it back.当 asm 语句移动堆栈指针而不放回它时,编译器会感到非常困惑。 It may look okay in isolation, but imagine the function being inlined;单独来看它可能看起来不错,但想象一下 function 被内联; the compiler can decide to move the asm with respect to apparently unrelated code.编译器可以决定移动汇编相对于明显不相关的代码。

Another problem is that because of the red zone , the compiler may have put important data below the stack pointer: right where your pushfq would overwrite it.另一个问题是,由于红色区域,编译器可能已将重要数据放在堆栈指针下方:就在您的pushfq将覆盖它的位置。

This is not so easy to resolve.这不是那么容易解决的。 My best guess would be我最好的猜测是

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

Either that or write it as a "naked" function purely in asm, so that you know the compiler isn't involved.要么将其写成纯粹在 asm 中的“裸” function,因此您知道不涉及编译器。

(As noted at https://stackoverflow.com/a/47402504/634919 , a minor code-size optimization can be made by writing add $-128, %%rsp ... sub $-128, %%rsp since -128 fits in sign-extended 8 bits, but +128 does not.) (如https://stackoverflow.com/a/47402504/634919所述,可以通过编写add $-128, %%rsp ... sub $-128, %%rsp rsp 来进行小的代码大小优化 - 128 适合符号扩展的 8 位,但 +128 不适合。)

(The sub/add will itself affect the arithmetic flags as noted below, but then again they get changed so often that it's hard to attach much meaning to their values. I suppose you could use lea -128(%%rsp), %%rsp if you really care.) sub/add本身会影响算术标志,如下所述,但是它们又经常被更改,以至于很难为它们的值附加太多意义。我想你可以使用lea -128(%%rsp), %%rsp如果你真的在乎,请lea -128(%%rsp), %%rsp 。)


As for the changing values, you're seeing changes in bits 2 and 6: the parity flag and zero flag.至于变化的值,您会看到第 2 位和第 6 位的变化:奇偶校验标志和零标志。 Since practically every arithmetic instruction sets these according to the result, and other code gets executed in between your calls (eg all the code of printf .) it is not surprising that we would see the values change, The carry, sign.由于实际上每条算术指令都根据结果设置这些,并且在您的调用之间执行其他代码(例如printf的所有代码。)我们会看到值发生变化(进位符号)也就不足为奇了。 overflow and auxiliary carry flags are similarly "volatile".溢出和辅助进位标志同样是“易失的”。 Nothing is weird about this.这没什么好奇怪的。

There's no reason to expect the value 0x2: all sorts of code has been running, and nearly all of it affects the flags, so why should all the other flags need to be clear?没有理由期望值 0x2:各种代码都在运行,几乎所有代码都会影响标志,那么为什么所有其他标志都需要清除呢?

If you like, you can single-step your code, instruction by instruction, in a debugger, and watch as RFLAGS changes.如果您愿意,您可以在调试器中逐条指令地单步执行您的代码,并观察 RFLAGS 的变化。 You would probably see it change hundreds of times between one printf and the next.您可能会看到它在一个 printf 和下一个之间变化了数百次。

So, why did I get 0x246 at the first time?那么,为什么我第一次得到 0x246 呢? Shouldn't be 0x2 according Intel's manual?根据英特尔的手册,不应该是 0x2 吗?

before flags_state() called first time, some code executed in system, as result most flags state is random, you can not assume any values on generic flags, like ZF (0x40) it can be and set and reset.. and how Intel's manual?在第一次调用flags_state()之前,一些代码在系统中执行,结果大多数标志 state 是随机的,您不能假设通用标志上的任何值,如ZF (0x40)它可以设置和重置..以及英特尔的手册? can be related here?可以在这里关联吗?

Why did it change when I try to print it more times and continue change?为什么当我尝试打印更多次并继续更改时它会发生变化?

function must not preserve ZF flag (unlike for instance DF in windows - must be 0 on return) - so which value this flag have after function return - also undefined - if only you by self not write all code on asm and gave full control over this. function must not preserve ZF flag (unlike for instance DF in windows - must be 0 on return) - so which value this flag have after function return - also undefined - if only you by self not write all code on asm and gave full control over这个。 by fact ZF is reset after flags_state return and not changed in prolog of flags_state - as result first time - you have value which is set in previous code and then already all time the same value, which set in flags_state (you wrong that it continue change - it not change already, as show your output - 0x206 all time)事实上ZFflags_state返回后被重置,并且在 flags_state 的flags_state中没有改变 - 作为第一次结果 - 你有在前面的代码中设置的值,然后已经在flags_state中设置了相同的值(你错了它继续改变- 它还没有改变,如您的 output - 0x206 一直显示)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM