Skip to main content
← All posts

Part 4: x86-64 Addressing Modes, Dereferencing & Scale-Index-Base (SIB)

Md. Harun-Or-RoshidNov 5, 20263 min readArticle
Part 4: x86-64 Addressing Modes, Dereferencing & Scale-Index-Base (SIB)

Lesson Overview & Architectural Focus

In assembly language, instructions compute, transfer data, and access memory using specific patterns known as addressing modes. Unlike RISC architectures that require separate load and store operations, x86-64 is a CISC architecture capable of calculating complex memory addresses directly inside instruction operands. In Part 4, we master immediate, register, and direct addressing, demystify pointer dereferencing with square brackets [...], break down RIP-relative addressing for Position-Independent Executables (PIE), and dissect the full hardware Scale-Index-Base (SIB) formula.

1. The Core Addressing Modes in x86-64

An operand in an x86-64 instruction can be specified in one of several fundamental ways:

Addressing ModeAssembly SyntaxData SourceExecution Speed
Immediatemov rax, 100Hardcoded literal embedded directly in instruction byte streamFastest (0 cycles)
Registermov rbx, raxDirect transfer from one CPU register to anotherFastest (< 1 cycle)
Direct (Absolute)mov rax, [0x401000]Direct 64-bit absolute memory pointer addressMemory Latency
Register Indirectmov rax, [rbx]Pointer dereference: reads memory address stored in RBXMemory Latency
Base + Displacementmov rax, [rbp - 16]Stack frame locals and structure/object field member offsetsMemory Latency
Scale-Index-Base (SIB)mov rax, [rdi + rcx*8 + 32]Hardware-accelerated array and multidimensional matrix indexingMemory Latency

2. The Scale-Index-Base (SIB) Effective Address Formula

The most versatile memory addressing mode in x86-64 is the Scale-Index-Base (SIB) mode. The CPU hardware Address Generation Unit (AGU) calculates the physical Effective Address (EA) in a single clock cycle according to the formula:

Effective Address = Base Register + (Index Register × Scale) + Displacement

Base & Index Registers

Any 64-bit general purpose register (RAX, RBX, RCX, RDX, RSI, RDI, RBP, R8-R15) can serve as the Base. Any GPR except RSP can serve as the Index (RSP is reserved as the stack pointer).

Scale Factors (1, 2, 4, 8)

The hardware multiplier (Scale) is restricted to powers of two matching standard primitive data types: 1 (bytes), 2 (16-bit words), 4 (32-bit dwords/floats), and 8 (64-bit qwords/pointers).

3. RIP-Relative Addressing for Position-Independent Code (PIC)

In 64-bit Linux, modern binaries are compiled as Position Independent Executables (PIE) to support Address Space Layout Randomization (ASLR). Absolute 64-bit memory addresses require 8-byte immediate encodings and break when sections load at randomized addresses.

To solve this, x86-64 introduced RIP-relative addressing: memory references calculate signed 32-bit displacements relative to the current Instruction Pointer (RIP). In NASM, this is expressed using the rel keyword or the default rel directive:

default rel ; Automatically enables RIP-relative addressing for all labels


section .data

table_base dq 0x10, 0x20, 0x30, 0x40


section .text

; Correct modern PIC pointer load:

lea rdi, [rel table_base] ; Resolves address offset relative to RIP

mov rax, [rdi + rcx*8] ; Access table element via SIB

4. MOV vs. LEA: The Pointer Arithmetic Secret

A common confusion for new assembly programmers is the distinction between MOV and LEA (Load Effective Address):

  • mov rax, [rbx + 16]: Dereferences memory. Fetches the 8-byte value stored at the memory location rbx + 16 and loads that value into RAX.
  • lea rax, [rbx + 16]: Does NOT access memory! It calculates the math formula inside the brackets (rbx + 16) and stores the resulting numerical address into RAX.

Optimizing compilers frequently exploit LEA as an ultra-fast arithmetic shortcut to perform addition and multiplication by 2, 4, 8, or 9 in a single instruction without touching CPU status flags (e.g. lea rax, [rdi + rdi*8] computes $x imes 9$).

Summary & Next Step

Addressing modes unlock the ability to traverse complex data structures, arrays, and objects in memory. In Part 5, we master data allocation directives: DB, DW, DD, DQ, uninitialized memory reservations in .bss, compile-time constants with EQU, and NASM preprocessor directives.

Share:
Share on X
Share on LinkedIn
Share on Facebook
Share on Reddit
Share on WhatsApp
Share on Telegram
Copy link

Related reading

x86-64 Addressing Modes & SIB Calculation | Draftsbook · Draftsbook