05 — Registers¶
Registers are the CPU's internal storage — the fastest memory available. Every computation happens through registers. Understanding their names, sizes, and conventions is fundamental.
x86-64 General-Purpose Registers¶
There are 16 general-purpose 64-bit registers. Each has multiple sub-register names that access different portions:
64-bit 32-bit 16-bit 8-bit(high) 8-bit(low)
────── ────── ────── ────────── ──────────
RAX EAX AX AH AL
RBX EBX BX BH BL
RCX ECX CX CH CL
RDX EDX DX DH DL
RSI ESI SI SIL
RDI EDI DI DIL
RSP ESP SP SPL
RBP EBP BP BPL
R8 R8D R8W R8B
R9 R9D R9W R9B
R10 R10D R10W R10B
R11 R11D R11W R11B
R12 R12D R12W R12B
R13 R13D R13W R13B
R14 R14D R14W R14B
R15 R15D R15W R15B
Accessing Sub-Registers¶
block-beta
columns 8
RAX["RAX (64-bit)"]:8
space:4 EAX["EAX (32-bit)"]:4
space:6 AX["AX (16-bit)"]:2
space:6 AH["AH (8-hi)"]:1 AL["AL (8-lo)"]:1
Important: Writing to 32-bit Zeros the Upper 32 Bits¶
Writing to EAX zero-extends into RAX. This is intentional x86-64 behavior.
Writing to AX, AH, AL does NOT zero the upper bits — only the selected portion changes.
Special-Purpose Registers¶
| Register | Name | Role |
|---|---|---|
| RSP | Stack Pointer | Points to the top of the stack |
| RBP | Base Pointer | Points to the base of the current stack frame |
| RIP | Instruction Pointer | Address of the next instruction to execute |
| RFLAGS | Flags Register | Status bits from arithmetic/logic operations |
RSP — Stack Pointer¶
- Always points to the last pushed value (top of stack)
- Stack grows downward — push decrements RSP, pop increments RSP
- Must be 16-byte aligned when calling external functions (System V ABI)
RBP — Base Pointer¶
- In traditional stack frames, RBP points to the saved RBP of the caller
- Modern code often uses frame pointer omission (
-fomit-frame-pointer) - Makes it easy to access local variables at
[rbp - offset]
Calling Convention Register Roles (System V AMD64 ABI)¶
When calling functions, registers have defined roles:
Argument Passing (first 6 integer arguments)¶
| Argument | Register |
|---|---|
| 1st | RDI |
| 2nd | RSI |
| 3rd | RDX |
| 4th | RCX |
| 5th | R8 |
| 6th | R9 |
| 7th+ | Pushed onto stack |
Return Value¶
| Register | Usage |
|---|---|
| RAX | Primary return value (integer/pointer) |
| RDX | Secondary return value (128-bit results) |
Caller-Saved (volatile) — Caller must save before calling¶
A called function may freely modify these.Callee-Saved (non-volatile) — Callee must preserve¶
If your function uses these, save them (PUSH) at entry and restore (POP) before returning.RFLAGS Register¶
RFLAGS is a 64-bit register where individual bits are meaningful:
| Bit | Flag | Abbreviation | Set When |
|---|---|---|---|
| 0 | Carry Flag | CF | Unsigned overflow |
| 2 | Parity Flag | PF | Even number of 1-bits in low byte |
| 4 | Auxiliary Carry | AF | Carry from bit 3 |
| 6 | Zero Flag | ZF | Result is zero |
| 7 | Sign Flag | SF | Result's MSB is 1 (negative) |
| 8 | Trap Flag | TF | Single-step debugging |
| 9 | Interrupt Enable | IF | Interrupts enabled |
| 10 | Direction Flag | DF | String operation direction |
| 11 | Overflow Flag | OF | Signed overflow |
You don't directly mov to RFLAGS. Arithmetic instructions set them automatically, and you read them with conditional jumps (je, jl, etc.) or setcc instructions.
Segment Registers (Legacy)¶
| Register | Name | Modern Use |
|---|---|---|
| CS | Code Segment | Implicitly used for code |
| DS | Data Segment | Implicitly used for data |
| SS | Stack Segment | Implicitly used for stack |
| ES | Extra Segment | String operations |
| FS | F Segment | Thread-local storage (TLS) |
| GS | G Segment | TLS on Windows, kernel data on Linux |
In 64-bit mode, segmentation is mostly disabled — CS, DS, SS, ES all have base 0. FS and GS are used for TLS by the OS/runtime.
SIMD Registers¶
Beyond general-purpose, x86-64 has wide registers for SIMD operations:
| Register Set | Width | Count | Extension |
|---|---|---|---|
MMX (MM0–MM7) |
64-bit | 8 | MMX (legacy) |
SSE (XMM0–XMM15) |
128-bit | 16 | SSE/SSE2–4 |
AVX (YMM0–YMM15) |
256-bit | 16 | AVX/AVX2 |
AVX-512 (ZMM0–ZMM31) |
512-bit | 32 | AVX-512 |
YMM0 contains XMM0 in its lower half. Writing XMM0 zeros the upper 128 bits of YMM0.
Register Usage in Practice¶
section .text
global _start
_start:
; Using sub-registers
mov rax, 0x1234567890ABCDEF
mov al, 0xFF ; low byte: rax = 0x1234567890ABCDFF
mov ah, 0xFF ; high byte of AX: rax = 0x1234567890ABFFFF
mov ax, 0xBEEF ; low word: rax = 0x1234567890ABBEEF
mov eax, 0 ; zero-extends: rax = 0x0000000000000000
; Caller/callee save example
push rbx ; save callee-saved register
mov rbx, 42 ; use it freely
pop rbx ; restore before returning
Checking Register State in GDB¶
(gdb) info registers ; all registers
(gdb) info registers rax rbx rcx ; specific registers
(gdb) p/x $rax ; print rax in hex
(gdb) p/d $rax ; print rax as signed decimal
(gdb) p/u $rax ; print rax as unsigned decimal
Key Takeaways¶
- 16 general-purpose 64-bit registers; each accessible at 64/32/16/8-bit widths
- Writing to 32-bit register (e.g.,
EAX) zero-extends to 64-bit (RAX) - Writing to 8/16-bit portions does NOT clear upper bits
- RDI, RSI, RDX, RCX, R8, R9 pass the first 6 function arguments
- RAX returns function results
- RBX, RBP, R12–R15 must be preserved across function calls