Chapter 4 Key Takeaways: Memory

The 13 Most Important Points from This Chapter


1. The virtual address space is a flat array of bytes. From the program's perspective, memory is 2^48 bytes addressed by a 64-bit integer. There is no type system, no bounds checking, no distinction between code and data at the hardware level. Structure (stack, heap, code, data) is imposed by the OS and linker, enforced by page table permissions.


2. Every process has the same four static segments. .text (read-execute: machine code), .rodata (read-only: constants/strings), .data (read-write: initialized globals), .bss (read-write: zero-initialized globals). BSS occupies no space in the ELF file — the OS zeroes it at load time. All four are set up by the linker from the ELF binary.


3. The stack grows toward lower addresses; RSP points to the current top. PUSH decrements RSP before storing. POP loads, then increments RSP. The stack top is always the lowest occupied address. Local variables are allocated by subtracting from RSP (sub rsp, N). The stack limit is typically 8MB; exceeding it causes a segfault.


4. RSP must be 16-byte aligned immediately before a CALL instruction. This is a hard requirement of the System V AMD64 ABI. After CALL pushes the 8-byte return address, RSP is 8 bytes below the pre-CALL value — which means RSP at function entry is offset 8 from 16-byte alignment. The standard prologue push rbp restores 16-byte alignment. Forgetting this causes crashes in functions that use SSE instructions.


5. .bss and .data differ in ELF file size, not runtime behavior. A 1MB buffer in .data makes the ELF file 1MB larger. The same buffer in .bss adds only a few bytes to the section header. Both are readable and writable at runtime, and both are zero-initialized by the OS for .bss (the OS guarantees zero initialization of anonymous pages).


6. Alignment requirements: 1-byte for bytes, 2 for words, 4 for dwords, 8 for qwords, 16 for SSE. Misaligned accesses on x86-64 usually work but may be slower (two cache lines loaded). Some SSE instructions (MOVAPS, MOVAPD, MOVDQA) require 16-byte alignment and will fault on misaligned addresses. AVX requires 32-byte, AVX-512 requires 64-byte. Use NASM's align directive to ensure section-level alignment.


7. A pointer is a 64-bit integer containing a virtual address. There is no pointer type distinction in assembly. A register containing an address is a pointer; any register can become a pointer by loading an address into it. LEA computes addresses without memory access; brackets [...] dereference (access memory at that address).


8. LEA is also an arithmetic instruction. lea rax, [rbx + rbx*2] computes rax = rbx * 3 without accessing memory, without modifying flags. lea rax, [rbx + 1] computes rax = rbx + 1 without modifying flags (unlike add). The compiler frequently uses LEA for both address computation and integer arithmetic.


9. Little-endian storage: the least-significant byte is at the lowest address. mov QWORD [addr], 0x0102030405060708 writes byte 0x08 to addr, 0x07 to addr+1, ..., 0x01 to addr+7. This is invisible to arithmetic but visible in GDB's x/8xb output and when parsing binary protocols. Use bswap to convert between little-endian and big-endian (network byte order).


**10. The $ symbol evaluates to the current address; $$` to the section start.** `msg_len equ $ - msg` computes the length of a string at assembly time — no runtime overhead. `times (N - ($ - $$) % N) % N db 0 pads the current position to an N-byte boundary within the section.


11. /proc/self/maps shows the complete virtual address space of the running process. Each line shows an address range, permissions (rwxp), file offset, device/inode, and the backing file (if any). The permissions column shows which of read/write/execute are allowed. The [stack] entry is rw-p (no execute — NX protection). This file is the ground truth for understanding where code and data actually live in a running process.


12. The return address lives on the stack, adjacent to local variables. The stack frame layout is: return address at [rbp+8], saved RBP at [rbp+0], then local variables at increasingly negative RBP offsets. These are consecutive memory locations. This adjacency is the foundation of stack buffer overflow vulnerabilities. The stack canary (a random value between local variables and the saved RBP/return address) is the primary mitigation.


13. NASM's data declaration directives map directly to memory bytes. db = 1 byte, dw = 2 bytes, dd = 4 bytes, dq = 8 bytes (initialized). resb/resw/resd/resq = BSS reservations. times N dir val = repeated initialization. equ = compile-time constant (no memory allocated). These map your source directly to the binary — no hidden overhead.


Visual Summary: Stack Frame Layout

High addresses (caller's frame above)
┌─────────────────────────────────┐ [rbp + N]   (caller's locals)
│    ...caller's locals...        │
├─────────────────────────────────┤ [rbp + 8]   ← return address (8 bytes)
│    RETURN ADDRESS               │              pushed by CALL
├─────────────────────────────────┤ [rbp + 0]   ← RBP (frame pointer)
│    SAVED RBP                    │              pushed in prologue
├─────────────────────────────────┤ [rbp - 8]
│    [canary if -fstack-protector] │
├─────────────────────────────────┤
│    local variable N             │ [rbp - N]
│    ...                          │
│    local variable 1             │
├─────────────────────────────────┤ ← RSP (top of stack, lowest address)
│    (unallocated)                │
Low addresses

Visual Summary: Section Permissions

Section    Permissions    Notes
─────────────────────────────────────────
.text      r-x            Code: readable, executable, not writable
.rodata    r--            Data: readable only (string literals, constants)
.data      rw-            Data: readable and writable (initialized globals)
.bss       rw-            Data: readable and writable (zero-initialized)
[heap]     rw-            Dynamic allocation (mmap/brk)
[stack]    rw-            Stack frames (NX: not executable!)
[vdso]     r-x            Kernel-provided user-space code