在过去的几天里,我一直在挣扎于一种奇怪的行为,试图获得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根据英特尔的手册?为什么当我尝试打印更多次并继续修改时,它会改变呢?
发布于 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所有时间)。
发布于 2021-06-10 03:39:36
__asm__ __volatile__("pushfq");
__asm__ __volatile__("pop %%rax": "=a"(flags));您不能像这样在asm语句之间拆分指令。当asm语句移动堆栈指针而不将其放回时,编译器会非常困惑。孤立地看上去可能没问题,但是想象一下函数是内联的;编译器可以决定针对明显无关的代码移动asm。
另一个问题是,由于red zone,编译器可能会将重要数据放在堆栈指针下面:就在您的pushfq将覆盖它的位置。
这不是那么容易解决的。我最好的猜测是
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之间更改了数百次。
https://stackoverflow.com/questions/67914113
复制相似问题