This is a comprehensive explanation of addressing in Linux ELF binaries, from the bytes on disk to runtime memory.
- The Three Address Spaces
- ELF File Structure
- Sections vs Segments
- Link-Time Virtual Addresses
- RIP-Relative Addressing in x86-64
- VA to File Offset Translation
- Runtime Loading
- Position-Independent Executables (PIE)
- ASLR (Address Space Layout Randomization)
- /proc/$pid/maps Explained
- Complete Worked Example
When working with ELF binaries, you must distinguish three different "addresses":
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β THREE DISTINCT ADDRESS SPACES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β 1. FILE OFFSET β
β βββββββββββββ β
β β’ Byte position within the ELF file on disk β
β β’ Range: 0 to (file_size - 1) β
β β’ Used by: hexdump, dd, file editors β
β β’ Example: 0x1000 means "1000th byte from start of file" β
β β
β 2. LINK-TIME VIRTUAL ADDRESS (LVA) β
β ββββββββββββββββββββββββββββββββ β
β β’ Address assigned by the linker (ld) β
β β’ Embedded in the ELF headers and symbol table β
β β’ What objdump, readelf, nm display β
β β’ For non-PIE: absolute addresses (e.g., 0x401000) β
β β’ For PIE: offsets from base 0 (e.g., 0x1000) β
β β
β 3. RUNTIME VIRTUAL ADDRESS (RVA) β
β βββββββββββββββββββββββββββββββ β
β β’ Actual address in process's virtual memory during execution β
β β’ What debuggers (gdb), /proc/pid/maps show β
β β’ For non-PIE: RVA == LVA β
β β’ For PIE: RVA == load_base + LVA β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
File offset β Virtual address (in general)
The ELF file is not a flat memory image. Different parts of the file get mapped to different virtual addresses, with potential gaps, different alignments, and some parts not mapped at all.
An ELF file has this general layout:
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β FILE OFFSET 0 β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ELF HEADER β
β β’ Magic: 0x7f 'E' 'L' 'F' β
β β’ Class: 32-bit or 64-bit β
β β’ Entry point (virtual address) β
β β’ Program header table offset (e_phoff) β
β β’ Section header table offset (e_shoff) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β PROGRAM HEADER TABLE β
β β’ Array of Elf64_Phdr structures β
β β’ Describes segments for runtime loading β
β β’ Each entry: type, offset, vaddr, paddr, filesz, memsz, flags β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β SEGMENT 1 β
β (might contain .text, .rodata, etc.) β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β SEGMENT 2 β
β (might contain .data, .got, etc.) β
β β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β ... β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β SECTION HEADER TABLE β
β β’ Array of Elf64_Shdr structures β
β β’ Describes sections for linking/debugging β
β β’ Often at end of file (or stripped away) β
ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
View with:
readelf -h binary # ELF header
readelf -l binary # Program headers (segments)
readelf -S binary # Section headersThis distinction is fundamental and often confused:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β SECTIONS vs SEGMENTS β
βββββββββββββββββββββββββββββββββββββ¬ββββββββββββββββββββββββββββββββββββββββββ€
β SECTIONS β SEGMENTS β
β (Linking View) β (Execution View) β
βββββββββββββββββββββββββββββββββββββΌββββββββββββββββββββββββββββββββββββββββββ€
β β’ Described by Section Headers β β’ Described by Program Headers β
β β’ Used by linker (ld) β β’ Used by loader (kernel, ld-linux.so) β
β β’ Fine-grained logical units β β’ Coarse-grained memory mappings β
β β’ Examples: β β’ Examples: β
β - .text (code) β - PT_LOAD (loadable segment) β
β - .rodata (read-only data) β - PT_DYNAMIC (dynamic linking info) β
β - .data (initialized data) β - PT_INTERP (interpreter path) β
β - .bss (uninitialized data) β - PT_GNU_STACK (stack permissions) β
β - .symtab (symbols) β β
β - .strtab (string table) β β
β - .rela.plt (relocations) β β
β β β
β β’ Can be stripped (optional) β β’ Required for execution β
β β’ `readelf -S` to view β β’ `readelf -l` to view β
βββββββββββββββββββββββββββββββββββββ΄ββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β
β PT_LOAD Segment (R-X) β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β .text β .plt β .rodata β ... β β β
β β (code) β (stubs) β (strings) β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
β PT_LOAD Segment (RW-) β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β .data β .got β .got.plt β .bss (no file bytes) β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
typedef struct {
Elf64_Word p_type; // Segment type (PT_LOAD, PT_DYNAMIC, ...)
Elf64_Word p_flags; // Permissions (PF_R, PF_W, PF_X)
Elf64_Off p_offset; // FILE OFFSET where segment starts
Elf64_Addr p_vaddr; // VIRTUAL ADDRESS where segment is mapped
Elf64_Addr p_paddr; // Physical address (usually ignored on Linux)
Elf64_Xword p_filesz; // Size of segment IN THE FILE
Elf64_Xword p_memsz; // Size of segment IN MEMORY (>= p_filesz)
Elf64_Xword p_align; // Alignment requirement
} Elf64_Phdr;Key fields for address translation:
p_offset: where in the filep_vaddr: where in virtual memoryp_filesz: how many bytes from filep_memsz: how many bytes in memory (extra bytes are zero-filled, e.g.,.bss)
gcc -no-pie -o hello hello.cThe linker assigns absolute virtual addresses:
$ readelf -l hello
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x0000000000000518 0x0000000000000518 R 0x1000
LOAD 0x0000000000001000 0x0000000000401000 0x0000000000401000
0x00000000000001bd 0x00000000000001bd R E 0x1000
LOAD 0x0000000000002000 0x0000000000402000 0x0000000000402000
0x0000000000000158 0x0000000000000158 R 0x1000
LOAD 0x0000000000002e10 0x0000000000403e10 0x0000000000403e10
0x0000000000000228 0x0000000000000230 RW 0x1000
Notice:
- VirtAddr starts at
0x400000(traditional x86-64 base) - File offset 0x1000 maps to VirtAddr 0x401000
- The relationship:
p_vaddr - p_offset = 0x400000(constant for these segments)
gcc -pie -o hello hello.c # or just: gcc -o hello hello.c$ readelf -l hello
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000000000 0x0000000000000000
0x0000000000000560 0x0000000000000560 R 0x1000
LOAD 0x0000000000001000 0x0000000000001000 0x0000000000001000
0x00000000000001d5 0x00000000000001d5 R E 0x1000
...
Notice:
- VirtAddr starts at
0x0(relative addresses) - File offset often equals VirtAddr (but not guaranteed)
- Actual runtime address = random base + VirtAddr
Your disassembly:
360400f: 4c 8d 05 ca 7f 99 04 lea 0x4997fca(%rip),%r8 # 7f9bfe0
Let's decode this:
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β RIP-RELATIVE LEA INSTRUCTION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Instruction at VA: 0x360400f β
β Instruction bytes: 4c 8d 05 ca 7f 99 04 (7 bytes) β
β β
β Breakdown: β
β ββββββββββ¬βββββββββ¬βββββββββ¬ββββββββββββββββββββββββββββββ β
β β 4c β 8d β 05 β ca 7f 99 04 β β
β β REX.WR β LEA β ModR/M β displacement (little-end) β β
β ββββββββββ΄βββββββββ΄βββββββββ΄ββββββββββββββββββββββββββββββ β
β β
β REX.W = 1: 64-bit operand size β
β REX.R = 1: extends ModR/M reg field (selects R8-R15) β
β 8d: LEA opcode β
β 05: ModR/M byte β
β - mod=00, reg=000, r/m=101 β RIP-relative addressing β
β - reg=000 with REX.R=1 β R8 β
β Displacement: 0x04997fca (little-endian: ca 7f 99 04) β
β β
β Calculation: β
β βββββββββββββ β
β Next instruction VA = 0x360400f + 7 = 0x3604016 β
β Target VA = Next_RIP + signed_displacement β
β = 0x3604016 + 0x4997fca β
β = 0x7f9bfe0 β
β β
β This matches objdump's comment: # 7f9bfe0 β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β WHY USE RIP-RELATIVE ADDRESSING? β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β 1. POSITION-INDEPENDENT CODE (PIC) β
β β’ Code can be loaded at any address β
β β’ The displacement is relative, not absolute β
β β’ Required for shared libraries (.so) β
β β’ Required for PIE executables β
β β
β 2. SMALLER CODE SIZE β
β β’ disp32 (4 bytes) vs absolute 64-bit address (8 bytes) β
β β’ Can't even encode 64-bit immediate in most instructions β
β β
β 3. NO RELOCATIONS NEEDED AT RUNTIME β
β β’ For code referencing code/rodata in same segment β
β β’ The relative offset is fixed at link time β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Common targets of lea ..(%rip), reg: β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β β’ String literals in .rodata β
β lea .LC0(%rip), %rdi # "Hello, world!\n" β
β call printf@PLT β
β β
β β’ Global/static variables β
β lea global_var(%rip), %rax β
β β
β β’ GOT entries (for external symbols in PIC) β
β mov stderr@GOTPCREL(%rip), %rax β
β β
β β’ Jump tables (switch statements) β
β lea .LJTI0_0(%rip), %rax β
β β
β β’ Function addresses β
β lea my_callback(%rip), %rdi β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β VIRTUAL ADDRESS β FILE OFFSET TRANSLATION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Given: target_va (e.g., 0x7f9bfe0) β
β β
β For each PT_LOAD segment: β
β if (target_va >= p_vaddr) AND (target_va < p_vaddr + p_filesz): β
β file_offset = p_offset + (target_va - p_vaddr) β
β FOUND! β
β β
β If no segment contains target_va within p_filesz: β
β β’ If within p_memsz but beyond p_filesz β it's .bss (no file bytes) β
β β’ Otherwise β invalid address β
β β
β Visual: β
β βββββββ β
β β
β File: β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β p_offset β β
β β β β β
β β βΌ β β
β β βββββββββββββββββββββββββ β β
β β β p_filesz β β β
β β βββββββββββββββββββββββββ β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β mapping β
β Memory: β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β p_vaddr β β
β β β β β
β β βΌ β β
β β βββββββββββββββββββββββββ¬βββββββ β β
β β β p_filesz β zero β β β
β β β (from file) β fill β β β
β β βββββββββββββββββββββββββ΄βββββββ β β
β β ββββββββ p_memsz βββββββββββββββΊ β β
β βββββββββββββββββββββββββββββββββββββββββββ β
β β
β The zero-fill region (p_memsz - p_filesz) is typically .bss β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
$ readelf -lW binary
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x3db4e18 0x3db4e18 R 0x1000
LOAD 0x3db5000 0x0000000003db5000 0x0000000003db5000 0x41ebea8 0x41ebea8 R E 0x1000
LOAD 0x7fa1000 0x0000000007fa1000 0x0000000007fa1000 0x003a000 0x003a000 R 0x1000
LOAD 0x7fdb580 0x0000000007fdc580 0x0000000007fdc580 0x0035490 0x0078cd0 RW 0x1000
Target VA: 0x7f9bfe0
Check each segment:
- Segment 1: 0x0 β€ 0x7f9bfe0 < 0x0 + 0x3db4e18? No (0x7f9bfe0 > 0x3db4e18)
- Segment 2: 0x3db5000 β€ 0x7f9bfe0 < 0x3db5000 + 0x41ebea8?
- Upper bound = 0x3db5000 + 0x41ebea8 = 0x7fa0ea8
- 0x7f9bfe0 < 0x7fa0ea8? Yes!
- Found in segment 2
file_offset = p_offset + (target_va - p_vaddr)
= 0x3db5000 + (0x7f9bfe0 - 0x3db5000)
= 0x3db5000 + 0x41e6fe0
= 0x7f9bfe0
In this case, file_offset equals VA (common for PIE with aligned segments).
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β EXECUTABLE LOADING PROCESS β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β 1. execve("./binary", argv, envp) system call β
β β
β 2. Kernel reads ELF header, validates magic β
β β
β 3. Kernel reads program headers (PT_LOAD segments) β
β β
β 4. For PIE/shared objects: kernel chooses random base address β
β For non-PIE: base = 0 (uses absolute addresses from ELF) β
β β
β 5. Kernel maps each PT_LOAD segment: β
β β
β For each PT_LOAD: β
β map_addr = base + p_vaddr (page-aligned down) β
β mmap(map_addr, p_memsz, prot_from_p_flags, β
β MAP_PRIVATE | MAP_FIXED, fd, p_offset_aligned) β
β β
β 6. If PT_INTERP exists (dynamically linked): β
β β’ Kernel loads the interpreter (ld-linux.so) β
β β’ Transfers control to interpreter β
β β’ Interpreter loads shared libraries, resolves symbols β
β β
β 7. If statically linked: β
β β’ Kernel transfers control directly to e_entry β
β β
β 8. Control reaches _start β __libc_start_main β main() β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PROCESS VIRTUAL ADDRESS SPACE β
β (simplified, not to scale) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β High addresses β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Kernel space β β
β β (not accessible to user) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β Stack β β growsβ
β β [rsp points here] β down β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β β β β
β β (unmapped gap) β β
β β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β mmap region β β
β β β’ Shared libraries (libc.so, etc.) β β
β β β’ Anonymous mappings β β
β β β’ File mappings β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β β β β
β β (unmapped gap) β β
β β β β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β Heap β β growsβ
β β [brk points here] β up β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β .bss (zero-init) β RW β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β .data (init data) β RW β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β .rodata β R β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β .text β R-X β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€ β
β β ELF headers β R β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β Load base address (0x400000 for non-PIE, random for PIE) β
β β
β Low addresses (0x0 typically unmapped - null pointer guard) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β POSITION-INDEPENDENT EXECUTABLE β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β PIE = executable that can be loaded at ANY address β
β β
β Characteristics: β
β βββββββββββββββββ β
β β’ ELF type: ET_DYN (same as shared libraries!) β
β β’ All code uses position-independent addressing β
β β’ Link-time VAs are offsets from 0 β
β β’ Kernel adds random base at load time β
β β
β Check if binary is PIE: β
β ββββββββββββββββββββββββ β
β $ readelf -h binary | grep Type β
β Type: DYN (Position-Independent Executable file) β PIE β
β Type: EXEC (Executable file) β non-PIE β
β β
β Or: β
β $ file binary β
β binary: ELF 64-bit LSB pie executable, ... β PIE β
β binary: ELF 64-bit LSB executable, ... β non-PIE β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β NON-PIE (Traditional Executable) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Compile: gcc -no-pie -o hello hello.c β
β β
β Link-time VA in ELF: 0x0000000000401000 (absolute) β
β Runtime VA: 0x0000000000401000 (same, always) β
β β
β β’ Code can use absolute addresses β
β β’ movabs $0x401000, %rax (valid but rare) β
β β’ Predictable addresses β easier exploitation β
β β’ ASLR only affects stack, heap, libraries (not main executable) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PIE (Position-Independent Executable) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Compile: gcc -pie -o hello hello.c (or just gcc, it's default) β
β β
β Link-time VA in ELF: 0x0000000000001000 (offset from base 0) β
β Runtime VA: 0x00005634a2001000 (random base + 0x1000) β
β β
β β’ All references must be relative (RIP-relative, GOT-relative) β
β β’ Unpredictable runtime addresses β harder exploitation β
β β’ Full ASLR protection β
β β
β Runtime address calculation: β
β ββββββββββββββββββββββββββββ β
β runtime_va = random_base + link_time_va β
β 0x5634a2001000 = 0x5634a2000000 + 0x1000 β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PIE CODE REFERENCING GLOBAL DATA β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Source: β
β ββββββββ β
β const char *msg = "Hello"; β
β printf("%s\n", msg); β
β β
β Generated code (simplified): β
β βββββββββββββββββββββββββββββ β
β lea .LC0(%rip), %rdi # address of "Hello" string β
β call printf@PLT β
β β
β Why this works at any load address: β
β ββββββββββββββββββββββββββββββββββββ β
β β
β At link time: β
β .text starts at VA 0x1000 β
β .rodata starts at VA 0x2000 β
β "Hello" is at VA 0x2050 β
β LEA instruction is at VA 0x1100 β
β displacement = 0x2050 - 0x1107 = 0xf49 (next instruction addr) β
β β
β At runtime (base = 0x7f0000000000): β
β LEA is at 0x7f0000001100 β
β RIP after LEA = 0x7f0000001107 β
β "Hello" is at 0x7f0000001107 + 0xf49 = 0x7f0000002050 β
β β
β The displacement (0xf49) is CONSTANT regardless of load address! β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ADDRESS SPACE LAYOUT RANDOMIZATION (ASLR) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ASLR randomizes base addresses of memory regions at load time. β
β This makes exploitation harder (attacker can't predict addresses). β
β β
β ββββββββββββββββββββββ¬ββββββββββββββββ¬ββββββββββββββββββββββββββββββββββ β
β β Component β ASLR affects? β Notes β β
β ββββββββββββββββββββββΌββββββββββββββββΌββββββββββββββββββββββββββββββββββ€ β
β β Stack β YES β Always randomized β β
β β mmap base β YES β Shared libs, anonymous maps β β
β β Heap (brk) β YES β Randomized offset from data β β
β β PIE executable β YES β Random base address β β
β β Non-PIE executable β NO β Fixed at 0x400000 β β
β β vDSO β YES β Virtual dynamic shared object β β
β β vsyscall page β Fixed β Legacy, at fixed address β β
β ββββββββββββββββββββββ΄ββββββββββββββββ΄ββββββββββββββββββββββββββββββββββ β
β β
β ASLR Configuration: β
β βββββββββββββββββββ β
β $ cat /proc/sys/kernel/randomize_va_space β
β 0 = ASLR disabled β
β 1 = Stack, mmap, VDSO randomized (not heap) β
β 2 = Full ASLR (stack, mmap, VDSO, heap) β default on modern Linux β
β β
β Disable for debugging: β
β βββββββββββββββββββββ β
β $ echo 0 | sudo tee /proc/sys/kernel/randomize_va_space β
β Or for single process: β
β $ setarch $(uname -m) -R ./binary β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ASLR ENTROPY ON x86-64 β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Typical entropy bits: β
β ββββββββββββββββββββ¬ββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββ β
β β Region β Entropy β Address range β β
β ββββββββββββββββββββΌββββββββββββββΌβββββββββββββββββββββββββββββββββββββββ€ β
β β Stack β ~30 bits β Varies by kernel config β β
β β mmap base β ~28 bits β Usually 0x7f0000000000 region β β
β β PIE executable β ~28 bits β Usually 0x5500000000 region β β
β β Heap β ~13 bits β Offset from end of data segment β β
β ββββββββββββββββββββ΄ββββββββββββββ΄βββββββββββββββββββββββββββββββββββββββ β
β β
β Example - Same PIE binary run 3 times: β
β ββββββββββββββββββββββββββββββββββββββ β
β Run 1: 0x556b3a401000-0x556b3a402000 r-xp /tmp/hello β
β Run 2: 0x55f8e2c01000-0x55f8e2c02000 r-xp /tmp/hello β
β Run 3: 0x561a44e01000-0x561a44e02000 r-xp /tmp/hello β
β β
β Notice: the offset within the mapping (0x1000) is constant, β
β but the base address (0x556b3a400000, etc.) changes. β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β /proc/$pid/maps FORMAT β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β address perms offset dev inode pathname β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β 55d3f4600000-55d3f4601000 r--p 00000000 08:01 1234567 /bin/hello β
β 55d3f4601000-55d3f4602000 r-xp 00001000 08:01 1234567 /bin/hello β
β 55d3f4602000-55d3f4603000 r--p 00002000 08:01 1234567 /bin/hello β
β 55d3f4603000-55d3f4604000 r--p 00002000 08:01 1234567 /bin/hello β
β 55d3f4604000-55d3f4605000 rw-p 00003000 08:01 1234567 /bin/hello β
β 7f1c23400000-7f1c23422000 r--p 00000000 08:01 7654321 /lib/x86_64-.../libc.so.6
β ... β
β 7ffc12345000-7ffc12366000 rw-p 00000000 00:00 0 [stack] β
β 7ffc123fe000-7ffc12402000 r--p 00000000 00:00 0 [vvar] β
β 7ffc12402000-7ffc12404000 r-xp 00000000 00:00 0 [vdso] β
β ffffffffff600000-ffffffffff601000 --xp 00000000 00:00 0 [vsyscall] β
β β
β Field breakdown: β
β βββββββββββββββββ β
β address: start-end virtual address range (RUNTIME addresses) β
β perms: r=read, w=write, x=execute, p=private/s=shared β
β offset: FILE OFFSET into the mapped file (for file-backed mappings) β
β dev: device major:minor (08:01 = /dev/sda1) β
β inode: inode number on that device β
β pathname: file path, or special region ([stack], [heap], [vdso], etc.) β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β UNDERSTANDING /proc/$pid/maps ENTRIES β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β Example: β
β 55d3f4601000-55d3f4602000 r-xp 00001000 08:01 1234567 /bin/hello β
β β
β This tells us: β
β βββββββββββββββββ β
β β’ Runtime VA range: 0x55d3f4601000 - 0x55d3f4602000 (4KB, one page) β
β β’ Permissions: read + execute, private mapping β
β β’ File offset: 0x1000 (this region comes from file offset 0x1000) β
β β’ The file: /bin/hello (inode 1234567 on device 08:01) β
β β
β Relationship to ELF: β
β βββββββββββββββββββ β
β This mapping corresponds to a PT_LOAD segment where: β
β p_offset = 0x1000 (matches "offset" field) β
β p_vaddr = 0x1000 (link-time, but runtime adds base) β
β p_flags = PF_R | PF_X (matches r-xp) β
β β
β Calculating base address: β
β βββββββββββββββββββββββββ β
β base = runtime_start - file_offset β
β = 0x55d3f4601000 - 0x1000 β
β = 0x55d3f4600000 β
β β
β This base is added to all link-time VAs to get runtime VAs. β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# For a running process
cat /proc/$(pidof myprogram)/maps
# Or while running under gdb
(gdb) info proc mappings
# Example useful one-liner: find base of main executable
grep -m1 'r--p.*hello$' /proc/$(pidof hello)/maps | cut -d'-' -f1Let's trace an address from disassembly through to runtime:
$ file mybinary
mybinary: ELF 64-bit LSB pie executable, x86-64, ...
$ readelf -lW mybinary | grep -A1 "Type.*LOAD"
LOAD 0x000000 0x0000000000000000 0x0000000000000000 0x001000 0x001000 R 0x1000
LOAD 0x001000 0x0000000000001000 0x0000000000001000 0x000500 0x000500 R E 0x1000
LOAD 0x002000 0x0000000000002000 0x0000000000002000 0x000200 0x000200 R 0x1000
LOAD 0x002e00 0x0000000000003e00 0x0000000000003e00 0x000250 0x000260 RW 0x1000$ objdump -drwC mybinary | grep lea.*rip
1150: 48 8d 3d a9 0e 00 00 lea 0xea9(%rip),%rdi # 2000 <__cxa_finalize@plt+0x1990>
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β COMPLETE ADDRESS RESOLUTION β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β STEP 1: Parse the instruction β
β βββββββββββββββββββββββββββββ β
β Instruction VA: 0x1150 (link-time) β
β Instruction length: 7 bytes β
β Displacement: 0x0ea9 (little-endian: a9 0e 00 00) β
β β
β STEP 2: Calculate target link-time VA β
β ββββββββββββββββββββββββββββββββββββββ β
β next_rip = 0x1150 + 7 = 0x1157 β
β target_va = next_rip + displacement = 0x1157 + 0xea9 = 0x2000 β
β (Matches objdump comment: # 2000) β
β β
β STEP 3: Find containing PT_LOAD segment β
β ββββββββββββββββββββββββββββββββββββββ β
β Target VA = 0x2000 β
β Check segment: p_vaddr=0x2000, p_filesz=0x200 β
β 0x2000 >= 0x2000 AND 0x2000 < 0x2200? YES β
β β
β STEP 4: Calculate file offset β
β ββββββββββββββββββββββββββββ β
β file_offset = p_offset + (target_va - p_vaddr) β
β = 0x2000 + (0x2000 - 0x2000) β
β = 0x2000 β
β β
β STEP 5: Read from file β
β βββββββββββββββββββββ β
β $ xxd -s 0x2000 -l 32 mybinary β
β 00002000: 4865 6c6c 6f2c 2057 6f72 6c64 210a 00.. Hello, World!... β
β β
β It's a string literal! β
β β
β STEP 6: At runtime (example /proc/pid/maps) β
β βββββββββββββββββββββββββββββββββββββββββββ β
β 55a1b2c00000-55a1b2c01000 r--p 00000000 08:01 123 /path/mybinary β
β 55a1b2c01000-55a1b2c02000 r-xp 00001000 08:01 123 /path/mybinary β
β 55a1b2c02000-55a1b2c03000 r--p 00002000 08:01 123 /path/mybinary β here β
β 55a1b2c03000-55a1b2c04000 r--p 00002000 08:01 123 /path/mybinary β
β 55a1b2c04000-55a1b2c05000 rw-p 00003000 08:01 123 /path/mybinary β
β β
β Base address: 0x55a1b2c00000 β
β Runtime VA of string: 0x55a1b2c00000 + 0x2000 = 0x55a1b2c02000 β
β β
β Verify in debugger: β
β (gdb) x/s 0x55a1b2c02000 β
β 0x55a1b2c02000: "Hello, World!\n" β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ADDRESS RELATIONSHIPS SUMMARY β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββββββββββββββ β
β β ELF File on Disk β β
β β (FILE OFFSETS) β β
β ββββββββββββ¬ββββββββββββ β
β β β
β objdump/readelf β PT_LOAD mappings define: β
β show LINK-TIME VAs β file_offset β vaddr relationship β
β β β
β ββββββββββββΌββββββββββββ β
β β Link-Time Virtual β β
β β Addresses (LVA) β β
β β (in ELF headers) β β
β ββββββββββββ¬ββββββββββββ β
β β β
β ββββββββββββββββββββββΌβββββββββββββββββββββ β
β β β β β
β Non-PIE PIE Shared Lib β
β (ET_EXEC) (ET_DYN) (ET_DYN) β
β β β β β
β RVA = LVA RVA = base + LVA RVA = base + LVA β
β (base=0) (base=random) (base=random) β
β β β β β
β ββββββββββββββββββββββΌβββββββββββββββββββββ β
β β β
β ββββββββββββΌββββββββββββ β
β β Runtime Virtual β β
β β Addresses (RVA) β β
β β (in /proc/pid/maps) β β
β ββββββββββββββββββββββββ β
β β
β β
β RIP-relative addressing: β
β βββββββββββββββββββββββββ β
β displacement = target_LVA - (instruction_LVA + instruction_length) β
β β
β This displacement is CONSTANT - works at any runtime base! β
β β
β At runtime: β
β target_RVA = (instruction_RVA + len) + displacement β
β = (base + instruction_LVA + len) + displacement β
β = base + target_LVA β β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
# View ELF header (entry point, type)
readelf -h binary
# View program headers (segments) - for VAβfile offset
readelf -lW binary
# View section headers
readelf -SW binary
# Disassemble with addresses
objdump -drwC binary
# Find all strings with file offsets
strings -t x binary
# View runtime mappings
cat /proc/$(pidof binary)/maps
# Read bytes at file offset
xxd -s 0x2000 -l 64 binary
dd if=binary bs=1 skip=$((0x2000)) count=64 2>/dev/null | xxd
# Check if PIE
file binary
readelf -h binary | grep Type
# Disable ASLR for debugging
setarch $(uname -m) -R ./binary
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space