Reading — step 1 of 5
Read
~1 min readELF Format
Program Headers (Segments)
Loader uses program headers to map file content into memory.
Each program header:
struct ProgramHeader {
uint32_t type; // PT_LOAD, PT_DYNAMIC, PT_INTERP, PT_GNU_STACK, ...
uint32_t flags; // R, W, X
uint64_t offset; // offset in file
uint64_t vaddr; // virtual address to load at
uint64_t paddr; // physical (rarely used)
uint64_t filesz; // bytes in file
uint64_t memsz; // bytes in memory (≥ filesz; rest zero-filled)
uint64_t align;
};
Common types:
- PT_LOAD: a segment to load. Most binaries have ~3 (text RX, rodata R, data RW).
- PT_DYNAMIC: the .dynamic section's contents (for dynamic linker).
- PT_INTERP: pathname of dynamic linker (e.g.
/lib64/ld-linux-x86-64.so.2). - PT_GNU_STACK: declares stack flags (executable or not).
- PT_GNU_RELRO: read-only after relocation.
- PT_NOTE: notes (ABI tags, build IDs).
Loading PT_LOAD segments:
- mmap
fileszbytes fromoffsettovaddrwith given prot (R/W/X). - If memsz > filesz: the rest is BSS (zero-filled). mmap anonymous + zero, or rely on file mapping + zero.
Example for /bin/cat:
TYPE OFFSET VADDR FILESZ MEMSZ FLAGS
PHDR 0040 0040 0001f8 0001f8 R
INTERP 0238 0238 00001c 00001c R
LOAD 0000 0000 0006a0 0006a0 R ← text+rodata
LOAD 1000 1000 003fad 003fad RX
LOAD 5000 5000 002250 002250 R
LOAD 7df8 8df8 0007e8 000940 RW ← data + bss
DYNAMIC ...
The kernel:
- Reads ELF header.
- Walks program headers, loads PT_LOAD segments.
- If PT_INTERP: load the dynamic linker (also ELF, recursive).
- Set rip to entry point.
- Jump.
For PIE: each load picks a base address (ASLR). vaddr is relative; final = base + vaddr.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…