如何直接读写x86标志寄存器?
据我所知,似乎有 9 个不同的标志。是否可以直接读取/更改它们?我知道我可以知道例如在执行 cmp/jmp 指令后是否设置了零标志,但我问是否可以做类似的事情
mov eax, flags
。
另外,对于书写,可以手动设置它们吗?
From what I've read, seems like there are 9 different flags. Is it possible to read/change them directly? I know I can know for example if the zero flag is set after doing a cmp/jmp instruction, but I'm asking if it's possible to do something like
mov eax, flags
or something.
Also, for writing, is it possible to set them by hand?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
某些标志可以通过特定说明直接设置或清除:
对于读写符号、零、辅助进位、奇偶校验和进位标志,可以使用 < a href="http://pdos.csail.mit.edu/6.828/2008/readings/i386/LAHF.htm" rel="noreferrer">LAHF 加载低 8 位(这 5 个标志加上3 个不确定位)写入 AH 寄存器,您可以使用 SAHF< /a> 将 AH 中的这些值存储回标志寄存器。
您还可以使用 PUSHF 指令推送标志到堆栈上,在堆栈上读取并修改它们,然后使用 POPF1 指令将它们存储回标志寄存器。
请注意,您无法使用 POPF 设置 VM 和 RF 标志 - 它们保留其先前的值。同样,只有在特权级别 0 执行时才能更改 I/O 特权级别,并且只有在至少与 I/O 特权级别相同的特权级别执行时才能更改中断标志。
脚注 1:
请注意,
popf
在现代 CPU 上相当慢;请参阅 Agner Fog 优化指南和说明表。它是微编码的,因为在内核模式下它能够更改 IF 和 AC 以及 IO 特权级别。无论当前 CPU 的模式如何,我们都会遭受损失,因为解码器对模式不敏感。如果可能,请使用 lahf/sahf 而不是 Pushf/popf 来提高性能,或者保存您关心的单个标志,例如
setc al
,然后再add al, 255
来设置CF = (AL!=0)
。或者setnc al
/sub al, 1
或其他。基于 0 或 1 寄存器设置或清除 SF 或 OF 的序列也很简单,无论是否反转标志。Some flags can be set or cleared directly with specific instructions:
For reading and writing the sign, zero, auxiliary carry, parity, and carry flags, you can use LAHF to load the lower 8 bits (those 5 flags plus 3 indeterminate bits) into the AH register, and you can use SAHF to store those values from AH back into the flags register.
You can also use the PUSHF instruction to push the flags onto the stack, read and modify them on the stack, and then use the POPF1 instruction to store them back into the flags register.
Note that you cannot set the VM and RF flags with POPF -- they retain their previous values. Similarly, you can only change the I/O privilege level when executing at privilege level 0, and the interrupt flag can only be changed when executing at a privilege level at least as privileged as the I/O privilege level.
Footnote 1:
Note that
popf
is quite slow on modern CPUs; see Agner Fog's optimization guide and instruction tables. It's microcoded because in kernel mode it's able to change IF and AC, and IO privilege level. We suffer the penalty regardless of mode on current CPUs because the decoders aren't mode-sensitive.If possible, use lahf/sahf instead of pushf/popf for performance, or save a single flag you care about like
setc al
then lateradd al, 255
to setCF = (AL!=0)
. Orsetnc al
/sub al, 1
or whatever. Sequences to set or clear SF or OF based on a 0 or 1 register are also straightforward, with/without inverting the flag.您可以使用 pushf 和 popf 指令将标志推入堆栈,您可以修改它们,然后将它们弹出。
You can use the pushf and popf instructions which will push the flags onto the stack, you can modify them, and then pop them back off.
如果只需要标志寄存器的低字节(包含SF、ZF、AF、PF、CF),那么有一个奇怪但方便的指令LAHF(哈哈),它将标志寄存器的低8位加载到AH 及其对应的 SAHF 将 AH 存储到标志中。
具体来说,对于进位标志,x86 提供了 CLC、STC 和 CMC,分别用于清除、设置和补充进位标志。
If you need only the lower byte of the flags register (which contains SF,ZF,AF,PF,CF), then there is the odd but convenient instruction LAHF (ha ha), which loads the bottom 8 bits of the flags register into AH, and its counterpart SAHF to store AH into flags.
For the carry flag specifically, x86 offers CLC, STC and CMC, to clear, set, and complement the carry flag, respectively.
最简单的方法是使用pushfd/pushfw 和pop (r16/r32)。
如果您想将
eflags
移至eax
,请使用下面的代码。或者,如果您只需要 eflags 中的前 16 位:
在大多数汇编器中(至少是 GAS 和 NASM,可能还有其他汇编器),没有显式操作数大小的
pushf
默认为您所处的模式。例如 64 位模式下的pushfq
。如果您想在 16 位模式下使用pushfd
来匹配 32 位pop
,通常只需要显式操作数大小。但这并没有什么坏处。Simplest way is using pushfd/pushfw and pop (r16/r32).
If you want to move
eflags
intoeax
, use code below.Or, if you only want the first 16 bits from
eflags
:In most assemblers (at least GAS and NASM, likely others),
pushf
without an explicit operand-size defaults to the mode you're in. e.g.pushfq
in 64-bit mode. You'd normally only need an explicit operand-size if you wanted to usepushfd
in 16-bit mode to match a 32-bitpop
. But it doesn't hurt.SETcc
该指令系列是观察某些标志/标志组合的另一种方法。
它根据各个标志设置字节的值。
例如,对于
CF
:可使用断言在 GitHub 上游运行。
Jcc
该指令系列当然是某些标志的另一种可能性,并且可用于实现
SETcc
:带有断言的可运行 GitHub 上游。
SETcc
This instruction family is another way to observe some flags / combination of flags.
It sets the value of a byte based on the individual FLAGS.
E.g., for
CF
:Runnable GitHub upstream with assertions.
Jcc
This instruction family is of course another possibility for certain flags, and could be used to implement
SETcc
:Runnable GitHub upstream with assertions.
将标志的副本保存在变量中以供妥善保管
<前><代码>.data
保存标志字节?
。代码
拉夫;将标志加载到 AH 中
mov saveflags,啊;将它们保存到变量中
SAHF:将 AH 存储到状态标志
检索之前存储的标志值
<前><代码>.code
mov 啊,saveflags ;将保存标志加载到 AH 中
萨赫夫;复制到标志寄存器中
Save a copy of the flags in a variable for safekeeping
SAHF: stores AH into status flags
Retrieve the value of flags stored earlier