Part 3: Memory Segmentation, Virtual Memory & Linux 64-Bit Address Space
Lesson Overview & Systems Architecture
Modern operating systems isolate processes in private 64-bit virtual memory spaces. Far from the simplistic 16-bit segmented real-mode model of early microprocessors, modern x86-64 long mode enforces flat memory models managed by hardware Page Tables and Memory Management Units (MMU). In Part 3, we dissect the anatomy of process memory, explore section semantics in NASM (.text, .rodata, .data, .bss), examine hardware page permissions (W^X / DEP), and inspect live memory mappings in Linux.
1. Real-Mode Segmentation vs. 64-Bit Flat Virtual Memory
In historic 16-bit architectures (like the Intel 8086), physical addresses were calculated via segment base shifting: Physical = (Segment << 4) + Offset. This permitted addressing up to 1 MB of memory through overlapping 64 KB segments (CS, DS, SS, ES).
In x86-64 Long Mode, segmentation is largely deprecated. The segment base registers (CS, DS, ES, SS) are forced to base zero by hardware, creating a contiguous Flat Memory Model. Only FS and GS remain active for thread-local storage (TLS) and kernel data structures. Modern processors implement a 48-bit or 57-bit virtual address space, translating virtual memory pages to physical RAM frames via 4-level or 5-level page tables managed by the CR3 control register.
2. Process Virtual Memory Map Layout in Linux
When the Linux kernel loads an ELF-64 binary into memory, it maps sections into distinct virtual address ranges protected by specific access permissions:
| Memory Segment | Permissions | Growth Direction | Contents & Architectural Role |
|---|---|---|---|
| Stack | RW (No Execute) | Downwards (↓ High to Low) | Function call frames, local variables, return addresses, parameter spilling (RSP) |
| Memory Mapping (MMAP) | R / W / X | Variable | Shared dynamic C libraries (libc.so), memory-mapped files, large memory allocations |
| Heap | RW | Upwards (↑ Low to High) | Dynamic memory allocations managed via sys_brk and userland allocators (malloc) |
| .bss (Block Started by Symbol) | RW | Static | Uninitialized global/static variables. Occupies 0 bytes in executable file on disk! |
| .data | RW | Static | Initialized global and static variables. Contained directly within executable file |
| .rodata | R (Read-Only) | Static | Immutable constants, string literals, jump tables. Writing triggers SIGSEGV |
| .text | R-X (Read + Execute) | Static | Binary executable machine instructions. Non-writable to prevent self-modifying code exploits |
3. Section Declarations in NASM
In NASM, you partition source code into sections using the section directive. Here is a practical template demonstrating all primary segments:
; ----------------------------------------------------------------------------
; memory_segments.asm
; ----------------------------------------------------------------------------
section .rodata
author_name db "Systems Engineer", 0
read_only_val dq 0xCAFEBABE12345678
section .data
counter dd 100 ; 32-bit initialized integer
status_flag db 1 ; 8-bit initialized boolean
section .bss
rx_buffer resb 4096 ; Reserve 4096 bytes uninitialized (zeroes)
thread_id resq 1 ; Reserve 1 quadword (8 bytes)
section .text
global _start
_start:
; Read from .rodata, increment counter in .data
mov eax, [counter]
inc eax
mov [counter], eax
; Terminate process
mov rax, 60
xor rdi, rdi
syscall
4. Security Defenses: W^X, DEP/NX Bit & ASLR
Modern silicon architectures implement the No-Execute (NX) bit (AMD) or Execute-Disable (XD) bit (Intel) within page table entries. This enforces the security policy known as W^X (Write XOR Execute): a page in memory may be writable, or executable, but never both simultaneously.
- Stack Execution Protection: Because the stack is marked writable, the MMU refuses to execute instructions located on it. If an attacker injects shellcode onto the stack via a buffer overflow, the CPU immediately generates a Hardware Fault (General Protection Fault / Segmentation Fault).
- ASLR (Address Space Layout Randomization): The operating system randomizes the virtual base addresses of the Stack, Heap, and dynamic libraries on every process execution, preventing deterministic pointer hijacking.
Summary & Next Step
Understanding memory sections provides the foundation for pointer manipulation. In Part 4, we explore x86-64 addressing modes, pointer dereferencing with brackets, and master the full Scale-Index-Base (SIB) effective addressing calculation formula.
Related reading
How to Give Google Antigravity Direct SSH Access to Remote Servers (Proxmox & Linux)
Connect Google Antigravity directly to Proxmox and Linux servers using the Model Context Protocol (MCP), dedicated Ed25519 keys, and automated updates.
By Md. Harun-Or-Roshid
Part 9: Load Balancing: Layer 4 vs Layer 7, Algorithms, Reverse Proxies & High Availability
Master high-availability load balancing, comparing Layer 4 transport vs Layer 7 application routing, consistent hashing rings, and health checks.
By Md. Harun-Or-Roshid
Part 10: Distributed Databases: Partitioning, Sharding, 2PC & CAP Theorem
Master distributed database architecture: horizontal sharding, consistent hashing, two-phase commit (2PC), CAP theorem trade-offs, and NewSQL engines.
By Md. Harun-Or-Roshid
Part 10: Multicore Systems: SMP, NUMA Architecture & Amdahl's Law Scaling
A study of multicore scalability, exploring Symmetric Multiprocessing (SMP), Non-Uniform Memory Access (NUMA), Amdahl's law, and on-chip mesh Networks-on-Chip.
By Md. Harun-Or-Roshid