Skip to content

Instantly share code, notes, and snippets.

@suntong
Created January 31, 2026 08:28
Show Gist options
  • Select an option

  • Save suntong/3738f9ca598dbacd29be6b15be29766b to your computer and use it in GitHub Desktop.

Select an option

Save suntong/3738f9ca598dbacd29be6b15be29766b to your computer and use it in GitHub Desktop.

Complete Guide to Linux ELF Addressing

This is a comprehensive explanation of addressing in Linux ELF binaries, from the bytes on disk to runtime memory.


Table of Contents

  1. The Three Address Spaces
  2. ELF File Structure
  3. Sections vs Segments
  4. Link-Time Virtual Addresses
  5. RIP-Relative Addressing in x86-64
  6. VA to File Offset Translation
  7. Runtime Loading
  8. Position-Independent Executables (PIE)
  9. ASLR (Address Space Layout Randomization)
  10. /proc/$pid/maps Explained
  11. Complete Worked Example

1. The Three Address Spaces

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                                       β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Critical Insight

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.


2. ELF File Structure

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 headers

3. Sections vs Segments

This 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                  β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Multiple Sections β†’ One Segment

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                                                                             β”‚
β”‚    PT_LOAD Segment (R-X)                                                    β”‚
β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚    β”‚  .text    β”‚   .plt   β”‚  .rodata  β”‚   ...   β”‚                    β”‚      β”‚
β”‚    β”‚  (code)   β”‚  (stubs) β”‚ (strings) β”‚         β”‚                    β”‚      β”‚
β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
β”‚                                                                             β”‚
β”‚    PT_LOAD Segment (RW-)                                                    β”‚
β”‚    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”      β”‚
β”‚    β”‚  .data  β”‚  .got  β”‚  .got.plt  β”‚  .bss (no file bytes)  β”‚        β”‚      β”‚
β”‚    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Program Header Structure (Elf64_Phdr)

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 file
  • p_vaddr: where in virtual memory
  • p_filesz: how many bytes from file
  • p_memsz: how many bytes in memory (extra bytes are zero-filled, e.g., .bss)

4. Link-Time Virtual Addresses

Non-PIE Executables (Traditional)

gcc -no-pie -o hello hello.c

The 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)

PIE Executables (Modern Default)

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

5. RIP-Relative Addressing in x86-64

The Instruction Encoding

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 RIP-Relative?

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    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                             β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

What Can RIP-Relative Address?

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚  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                                           β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

6. VA to File Offset Translation

The Algorithm

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚             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               β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Worked Example

$ 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:

  1. Segment 1: 0x0 ≀ 0x7f9bfe0 < 0x0 + 0x3db4e18? No (0x7f9bfe0 > 0x3db4e18)
  2. 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).


7. Runtime Loading

The Kernel's execve() Process

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     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()                     β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Memory Layout After Loading

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    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)                β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

8. Position-Independent Executables (PIE)

What is PIE?

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    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               β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

PIE vs Non-PIE Address Layout

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     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                                   β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

How PIE Code References Data

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚               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!           β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

9. ASLR (Address Space Layout Randomization)

What ASLR Randomizes

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              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

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        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.                       β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

10. /proc/$pid/maps Explained

Format

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                        /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.)    β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Key Observations

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚              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.                β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Reading Maps for Analysis

# 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'-' -f1

11. Complete Worked Example

Let's trace an address from disassembly through to runtime:

The Binary

$ 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

The Instruction

$ objdump -drwC mybinary | grep lea.*rip

1150:   48 8d 3d a9 0e 00 00    lea    0xea9(%rip),%rdi        # 2000 <__cxa_finalize@plt+0x1990>

Step-by-Step Address Resolution

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                    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"                                          β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Summary Diagram

β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚                     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   βœ“                                         β”‚
β”‚                                                                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Quick Reference Commands

# 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment