首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >EFLAGS状况

EFLAGS状况
EN

Stack Overflow用户
提问于 2021-06-10 03:05:57
回答 2查看 733关注 0票数 0

在过去的几天里,我一直在挣扎于一种奇怪的行为,试图获得EFLAGS的状态。为了实现这一点,我编写了以下代码:

代码语言:javascript
复制
#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;

}

当它运行时,我得到:

代码语言:javascript
复制
./flags
Returning EFLAGS state: 0x246

当我把旗子打印两次的时候,会变得越来越奇怪

代码语言:javascript
复制
Returning EFLAGS state: 0x246
Returning EFLAGS state: 0x206

当我把它打印出来6次时,它变了

代码语言:javascript
复制
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次时,最奇怪的(至少对我来说)

代码语言:javascript
复制
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根据英特尔的手册?为什么当我尝试打印更多次并继续修改时,它会改变呢?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-06-10 08:36:58

那么,为什么我第一次得到0x246呢?不应该是0x2根据英特尔的手册?

flags_state()第一次调用之前,系统中执行的一些代码,由于大多数标志状态是随机的,您不能在泛型标志上假设任何值,比如可以设置和重置的ZF (0x40)。英特尔的手册是怎么写的?能在这里有关联吗?

,当我尝试打印更多次并继续更改时,为什么它会改变呢?

函数不能保留ZF标志(例如,windows中的DF --返回时必须为0)--因此,如果您自己没有在DF上编写所有代码并完全控制它,那么函数返回后的值也是未定义的。事实上,ZF是在flags_state返回后重置的,并且在flags_state的prolog中没有更改--结果是第一次--您的值是在以前的代码中设置的,然后一直都是在flags_state中设置的相同的值(您错了,它继续更改了-它没有改变,显示您的输出- 0x206所有时间)。

票数 1
EN

Stack Overflow用户

发布于 2021-06-10 03:39:36

代码语言:javascript
复制
  __asm__ __volatile__("pushfq");
  __asm__ __volatile__("pop %%rax": "=a"(flags));

您不能像这样在asm语句之间拆分指令。当asm语句移动堆栈指针而不将其放回时,编译器会非常困惑。孤立地看上去可能没问题,但是想象一下函数是内联的;编译器可以决定针对明显无关的代码移动asm。

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

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

代码语言:javascript
复制
unsigned long get_rflags(void) {
    unsigned long result;
    asm("sub $128, %%rsp ; pushfq ; pop %0 ; add $128, %%rsp" 
        : "=r"  (result) : : "cc");
    return result;
}

或者将它写成一个纯粹在asm中的“裸”函数,这样您就可以知道编译器是不涉及到的。

(正如在https://stackoverflow.com/a/47402504/634919上所指出的,可以通过编写add $-128, %%rsp . sub $-128, %%rsp来进行代码大小的小优化,因为-128适合符号扩展的8位,但+128不适合。)

(如下面所述,sub/add本身将影响算术标志,但它们又经常被更改,因此很难对它们的值附加很大的意义。如果你真的关心的话,我想你可以使用lea -128(%%rsp), %%rsp。)

至于变化的值,您将看到第2位和第6位的变化:奇偶校验标志和零标志。因为实际上每个算术指令都会根据结果设置这些指令,并且在调用之间执行其他代码(例如,printf的所有代码!)毫不奇怪,我们会看到价值观的改变。进位标志、标志标志、溢出标志和辅助进位标志同样是“易失性”的。这没什么奇怪的。

没有理由期望值0x2:所有类型的代码都在运行,几乎所有的代码都会影响标志,那么为什么所有其他标志都需要清楚?

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

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67914113

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档