While program headers are for the loader, section headers are for tools — the linker, debugger, readelf, objdump. They describe the logical structure of the file: where code lives, where symbols are defined, where relocations need to be applied.
Think of section headers as a detailed index of the file. Each entry describes one section with its name, type, flags, address, offset, and size.
Each 64-bit section header entry is 64 bytes:
| Field | Size | Description |
|---|---|---|
| sh_name | 4 | Index into section name string table |
| sh_type | 4 | Section type (SHT_PROGBITS=1, SHT_SYMTAB=2, SHT_STRTAB=3, ...) |
| sh_flags | 8 | SHF_WRITE=1, SHF_ALLOC=2, SHF_EXECINSTR=4 |
| sh_addr | 8 | Virtual address (0 if not loaded) |
| sh_offset | 8 | File offset |
| sh_size | 8 | Section size in bytes |
| sh_link | 4 | Link to related section |
| sh_info | 4 | Additional info |
| sh_addralign | 8 | Alignment |
| sh_entsize | 8 | Entry size (for tables) |
The e_shstrndx field in the ELF header points to the section that contains all section names (typically .shstrtab).
$ readelf -S /bin/ls | head -15 Section Headers: [Nr] Name Type Address Offset Size [ 0] NULL 0000000000000000 00000000 000000 [ 1] .interp PROGBITS 00000000000002b8 000002b8 00001c [ 2] .note.gnu.property NOTE 00000000000002d8 000002d8 000020 [ 3] .gnu.hash GNU_HASH 00000000000002f8 000002f8 0000b0 [ 4] .dynsym DYNSYM 00000000000003a8 000003a8 000180 [ 5] .dynstr STRTAB 0000000000000528 00000528 0000c0 [ 6] .gnu.version VERNEED 00000000000005e8 000005e8 00003c [ 7] .gnu.version_r VERNEED 0000000000000624 00000624 000060
What does the sh_addr field represent?
A section has sh_offset=0x1000 and sh_size=0x200. How many bytes of the ELF file does it occupy?
Section headers describe individual sections. The next concept covers the most common sections you'll encounter.