How to read and write x86 flags registers directly?
Solution 1
Some flags can be set or cleared directly with specific instructions:
- CLC, STC, and CMC: clear, set, and complement the carry flag
- CLI and STI: clear and set the interrupt flag (which should be done atomically)
- CLD and STD: clear and set the direction flag
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 later add al, 255
to set CF = (AL!=0)
. Or setnc 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.
Solution 2
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.
Solution 3
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.
Solution 4
Simplest way is using pushfd/pushfw and pop (r16/r32).
If you want to move eflags
into eax
, use code below.
pushfd # push eflags into stack
pop %eax # pop it into %eax
Or, if you only want the first 16 bits from eflags
:
pushfw
pop %ax
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 use pushfd
in 16-bit mode to match a 32-bit pop
. But it doesn't hurt.
Solution 5
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
:
stc
setc al
; al == 1
clc
setc al
; al == 0
Runnable GitHub upstream with assertions.
Jcc
This instruction family is of course another possibility for certain flags, and could be used to implement SETcc
:
jc set
mov al, 0
jmp end
set:
mov al, 1
end:
Related videos on Youtube
devoured elysium
Updated on July 09, 2022Comments
-
devoured elysium almost 2 years
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?
-
Peter Cordes over 6 years
-
Peter Cordes about 6 yearsRelated: How can I set or clear overflow flag in x86 assembly? for clearing / setting OF with either PUSHF/AND/POPF, or an instructions sequence that writes all flags including OF with a known values.
-
-
Ciro Santilli OurBigBook.com about 9 yearsMore precisely: the Intel manual classifies each flag as either: status, control or system. The system flags cannot be modified by userland applications.
-
Peter Cordes over 5 yearsWhy would you mov+add instead of
pop %eax
like a normal person? POP is faster and smaller than MOV + ADD. -
ecm about 2 years
pushf
defaults topushfw
in a 16-bit code segment but may be an alias topushfd
in a 32-bit code segment. -
Peter Cordes about 2 years@babu646: your edit implies that
pushf
is always 16-bit. It's not. GAS assemblespushf
to use an operand-size matching the mode, i.e. just to a9c
byte. So ingcc -m32 -c foo.s
, it assembles aspushfd
. Or in-m64
code, it assembles aspushfq
(still just a9c
byte). Same for NASM-felf32
vs.-felf64
. (This answer uses AT&T.) There might be some assemblers wherepushf
is a 16-bit push even in 32 or 64-bit mode, but not GAS or NASM. Or maybe you're assuming 16-bit mode wherepushf
is alwayspushfw
, andpop %eax
would need an operand-size prefix. -
Peter Cordes about 2 years@ecm: That mistake was introduced by an edit. I've made an edit to fix it.