Bru*_*ado 0 assembly x86-64 inline-assembly 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;
}
Run Code Online (Sandbox Code Playgroud)
当它运行时,我得到:
./flags
Returning EFLAGS state: 0x246
Run Code Online (Sandbox Code Playgroud)
当我打印两次标志时,它变得更奇怪了
Returning EFLAGS state: 0x246
Returning EFLAGS state: 0x206
Run Code Online (Sandbox Code Playgroud)
当我尝试打印 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
Run Code Online (Sandbox Code Playgroud)
最后是最奇怪的(至少对我来说),当我打印 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
Run Code Online (Sandbox Code Playgroud)
那么,为什么我第一次得到 0x246 呢?根据英特尔的手册不应该是 0x2 吗?为什么当我尝试打印更多次并继续更改时它会改变?
__asm__ __volatile__("pushfq");
__asm__ __volatile__("pop %%rax": "=a"(flags));
Run Code Online (Sandbox Code Playgroud)
你不能像这样分解 asm 语句之间的指令。当 asm 语句移动堆栈指针而不将其放回时,编译器会非常困惑。孤立地看可能没问题,但想象一下函数是内联的;编译器可以决定针对明显不相关的代码移动 asm。
另一个问题是,由于红色区域,编译器可能将重要数据放在堆栈指针下方:就在您pushfq会覆盖它的位置。
这不是那么容易解决的。我最好的猜测是
unsigned long get_rflags(void) {
unsigned long result;
asm("sub $128, %%rsp ; pushfq ; pop %0 ; add $128, %%rsp"
: "=r" (result) : : "cc");
return result;
}
Run Code Online (Sandbox Code Playgroud)
要么将其写为纯粹在 asm 中的“裸”函数,以便您知道不涉及编译器。
(如/sf/answers/3318175311/ 所述,可以通过编写add $-128, %%rsp...进行较小的代码大小优化...sub $-128, %%rsp因为 -128 适合符号扩展的 8 位,但 +128 不适合。)
(sub/add意志本身会影响如下所述的算术标志,但是它们又经常改变,以至于很难给它们的值赋予太多意义。我想lea -128(%%rsp), %%rsp如果你真的关心的话,你可以使用。)
至于变化的值,您会看到第 2 位和第 6 位的变化:奇偶校验标志和零标志。由于几乎每个算术指令都根据结果设置这些值,并且在您的调用之间执行其他代码(例如printf!的所有代码),因此我们会看到值发生变化也就不足为奇了。进位、符号、溢出和辅助进位标志同样是“易失的”。这没有什么奇怪的。
没有理由期望值 0x2:所有类型的代码都在运行,并且几乎所有代码都会影响标志,那么为什么所有其他标志都需要清除?
如果您愿意,您可以在调试器中逐条指令地单步执行代码,并观察 RFLAGS 的变化。您可能会看到它在一个 printf 和下一个之间变化数百次。
| 归档时间: |
|
| 查看次数: |
74 次 |
| 最近记录: |