Post

6. Flags

Welcome to one of the most important concepts in assembly programming! Flags are the CPU’s way of telling you what happened during the last operation. They’re the secret sauce that makes decision-making and loops possible.

What are Flags?

The RFLAGS register is a 64-bit register where each bit represents a different condition or status. Its kinda the CPU’s dashboard with status eg. Negative, Zero, Carry, Overflow. These flags are automatically set after most arithmetic and logical operations.

The RFLAGS Register

Here are the most important flags we’ll work with:

Refer this for more detailed view.

FlagBitNamePurpose
CF0Carry FlagSet if unsigned overflow occurred
PF2Parity FlagSet if result has even number of 1 bits
AF4Adjust FlagBCD operations (rarely used)
ZF6Zero FlagSet if result was zero
SF7Sign FlagSet if result was negative
OF11Overflow FlagSet if signed overflow occurred

How Flags Get Set

After every arithmetic operation, the CPU automatically updates these flags:

1
2
3
4
5
6
7
8
9
10
mov rax, 10
mov rbx, 20

; After this addition, flags will be:
add rax, rbx    ; ZF=0 (result  0), SF=0 (positive), CF=0 (no carry), OF=0 (no overflow)

sub rax, 30     ; ZF=1 (result = 0), SF=0, CF=0, OF=0

mov rcx, -1
add rcx, 1      ; ZF=1 (result = 0), SF=0, CF=1 (unsigned overflow), OF=0

Let’s this practically in rappel we discussed in previous blogs.

After we execute the sub rax, 30 instruction, we can see ZF is set.

> sub rax, 30
rax=0000000000000000 rbx=0000000000000014 rcx=0000000000000000
rdx=0000000000000000 rsi=0000000000000000 rdi=0000000000000000
rip=0000000000400005 rsp=00007fffda9cd5f0 rbp=0000000000000000
 r8=0000000000000000  r9=0000000000000000 r10=0000000000000000
r11=0000000000000000 r12=0000000000000000 r13=0000000000000000
r14=0000000000000000 r15=0000000000000000
[cf=0, zf=1, of=0, sf=0, pf=1, af=0, df=0]

In GDB/Pwndbg, you can see the current flags:

1
2
3
4
5
6
7
8
9
10
11
12
pwndbg> info registers eflags
eflags = 0x202 [ CF PF IF ]

pwndbg> p $eflags
$1 = [ CF ZF ]

# Set flags
# setflag <flag_name> <value>
# To set the Zero Flag (ZF):
pwndbg> setflag ZF 1
# To clear the Carry Flag (CF):
pwndbg> setflag CF 0

The CMP Instruction: Comparison Without Storage

The cmp instruction is specifically designed for comparisons. It subtracts the operands but doesn’t store the result - it only sets the flags!

1
2
3
4
5
mov rax, 10
mov rbx, 20

cmp rax, rbx    ; Sets flags as if we did: 10 - 20
; Result: ZF=0 (not equal), SF=1 (negative), CF=1 (borrow needed)

You can run this in rappel and check. But if you want to make it full assembly program then you need to add assembler directives and entry point _start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
section .data
    ; No data needed for this simple comparison

section .bss
    ; No uninitialized data

section .text
    global _start

_start:
    mov rax, 10          ; Load 10 into RAX
    mov rbx, 20          ; Load 20 into RBX

    cmp rax, rbx         ; Compare RAX and RBX (sets flags as if RAX - RBX)

    mov rax, 60           ; syscall: exit
    syscall

1
2
3
$ nasm -f elf64 cmp.s -o cmp.o
$ ld cmp.o -o cmp
$ ./cmp

CMP is equivalent to SUB, but doesn’t change the operands.

TEST Instruction: Bitwise Comparison

test performs a bitwise AND without storing the result, only setting flags:

1
2
3
4
5
6
mov rax, 5      ; Binary: 101
test rax, 1     ; Check if least significant bit is set
; ZF=0 (result of AND is not zero), so number is odd

test rax, rax   ; Common idiom: check if RAX is zero
; ZF=1 if RAX is zero, ZF=0 otherwise

Why Flags Matter: Conditional Jumps

Flags become incredibly powerful when combined with conditional jumps (next chapter). They enable if statements and loops.

Some instructions don’t affect flags:

  • mov, lea, push, pop

Some instructions do affect flags:

  • add, sub, cmp, test
This post is licensed under CC BY 4.0 by the author.