x86-64 Registers
The Register File
All x86-64 registers
GENERAL PURPOSE (16 registers × 64-bit):
┌────────┬────────┬───────┬──────┬───────────────────────────────────┐
│ 64-bit │ 32-bit │ 16-bit│ 8-bit│ System V ABI Role │
├────────┼────────┼───────┼──────┼───────────────────────────────────┤
│ RAX │ EAX │ AX │ AL │ Return value, syscall number │
│ RBX │ EBX │ BX │ BL │ Callee-saved (preserved) │
│ RCX │ ECX │ CX │ CL │ 4th arg, shift count, loop ctr │
│ RDX │ EDX │ DX │ DL │ 3rd arg, I/O port, mul/div high │
│ RSI │ ESI │ SI │ SIL │ 2nd arg, source index │
│ RDI │ EDI │ DI │ DIL │ 1st arg, destination index │
│ RBP │ EBP │ BP │ BPL │ Base pointer (callee-saved) │
│ RSP │ ESP │ SP │ SPL │ Stack pointer (callee-saved) │
│ R8 │ R8D │ R8W │ R8B │ 5th arg │
│ R9 │ R9D │ R9W │ R9B │ 6th arg │
│ R10 │ R10D │ R10W │ R10B │ Scratch, 4th syscall arg │
│ R11 │ R11D │ R11W │ R11B │ Scratch, clobbered by syscall │
│ R12 │ R12D │ R12W │ R12B │ Callee-saved │
│ R13 │ R13D │ R13W │ R13B │ Callee-saved │
│ R14 │ R14D │ R14W │ R14B │ Callee-saved │
│ R15 │ R15D │ R15W │ R15B │ Callee-saved │
└────────┴────────┴───────┴──────┴───────────────────────────────────┘
SPECIAL PURPOSE:
RIP - Instruction pointer (cannot mov to it directly)
RFLAGS - Status flags register
FS/GS - Segment registers (TLS, stack canary)
SSE/AVX (16 registers × 128/256-bit):
XMM0-XMM15 (128-bit, SSE)
YMM0-YMM15 (256-bit, AVX — extends XMM)
Float args: XMM0-XMM7
Float return: XMM0
Sub-Register Access
How sub-registers map
RAX (64-bit):
┌──────────────────────────────────────────────────────────────────┐
│ RAX │
│ ┌─────────────────────────────────────────────┤
│ │ EAX │
│ │ ┌────────────────────────┤
│ │ │ AX │
│ │ ├───────────┬────────────┤
│ │ │ AH │ AL │
└────────────────────┴────────────────────┴───────────┴────────────┘
63 32 16 8 0
R8 (64-bit):
┌──────────────────────────────────────────────────────────────────┐
│ R8 │
│ ┌─────────────────────────────────────────────┤
│ │ R8D │
│ │ ┌────────────────────────┤
│ │ │ R8W │
│ │ │ ┌───────────┤
│ │ │ │ R8B │
└────────────────────┴────────────────────┴────────────┴───────────┘
NOTE: R8-R15 do NOT have AH-equivalent high-byte access.
Critical zero-extension rule
; Writing to 32-bit register ZEROS upper 32 bits
mov rax, 0xFFFFFFFFFFFFFFFF
mov eax, 0x00000001 ; RAX = 0x0000000000000001 (zeroed!)
; Writing to 16-bit or 8-bit does NOT zero upper bits
mov rax, 0xFFFFFFFFFFFFFFFF
mov ax, 0x0001 ; RAX = 0xFFFFFFFFFFFF0001 (preserved!)
mov al, 0x02 ; RAX = 0xFFFFFFFFFFFF0002 (preserved!)
; This is WHY compilers prefer 32-bit operations:
; xor eax, eax is better than xor rax, rax (smaller encoding, same effect)
RFLAGS Register
Flag bits
Bit Name Full Name Set When
──────────────────────────────────────────────────────────────
0 CF Carry Flag Unsigned overflow/underflow
2 PF Parity Flag Low byte has even number of 1s
4 AF Adjust Flag BCD carry from bit 3
6 ZF Zero Flag Result is zero
7 SF Sign Flag Result MSB is 1 (negative signed)
10 DF Direction Flag String ops decrement (std/cld)
11 OF Overflow Flag Signed overflow
Which instructions affect which flags
; Instructions that SET flags:
; add, sub, cmp, test, and, or, xor, inc, dec, neg, shl, shr, sar, mul, imul
; Instructions that DO NOT affect flags:
; mov, lea, push, pop, call, ret, jmp, nop
; INC/DEC do NOT affect CF (carry flag) — use ADD/SUB if you need CF
; TEST sets flags like AND but discards result:
test rax, rax ; ZF=1 if RAX==0, SF=1 if negative
; CMP sets flags like SUB but discards result:
cmp rax, 10 ; ZF=1 if RAX==10, CF=1 if RAX < 10 (unsigned)
Segment Registers (FS/GS)
Thread-local storage
; FS and GS are used for thread-local storage in Linux
; Stack canary (loaded from TLS via FS)
mov rax, QWORD PTR fs:[0x28] ; Load canary value
; This is why buffer overflows are detected — canary changes per thread
; GS is used in kernel mode for per-CPU data
; In GDB:
; (gdb) print/x $fs_base — show FS base address
; (gdb) x/gx $fs_base+0x28 — show canary value
Register Usage Conventions
System V AMD64 ABI summary
CALLER-SAVED (scratch — function may destroy):
RAX - return value
RCX - 4th integer argument
RDX - 3rd integer argument
RSI - 2nd integer argument
RDI - 1st integer argument
R8 - 5th integer argument
R9 - 6th integer argument
R10 - scratch
R11 - scratch
CALLEE-SAVED (preserved — function must restore):
RBX, RBP, R12, R13, R14, R15, RSP
SYSCALL CONVENTION (different from function calls!):
RAX - syscall number / return value
RDI - 1st argument
RSI - 2nd argument
RDX - 3rd argument
R10 - 4th argument (NOT RCX!)
R8 - 5th argument
R9 - 6th argument
RCX, R11 - clobbered by syscall instruction
Choosing registers
; Need to survive a function call? Use callee-saved:
push r12
mov r12, rdi ; Save argument in r12
call some_function ; r12 survives!
mov rdi, r12 ; Use saved value
pop r12
; Quick scratch work? Use caller-saved:
mov r10, rax ; Temporary, no need to save
shl r10, 3
add rax, r10 ; rax = rax * 9