When you run readelf -S, you see dozens of sections. Understanding the most common ones lets you navigate any ELF file and know what you're looking at.
Sections are like labeled boxes in a warehouse:
| Section | Type | Flags | Contains |
|---|---|---|---|
| .text | SHT_PROGBITS | SHF_ALLOC + SHF_EXECINSTR | Machine code |
| .rodata | SHT_PROGBITS | SHF_ALLOC | Read-only constants |
| .data | SHT_PROGBITS | SHF_ALLOC + SHF_WRITE | Initialized global variables |
| .bss | SHT_NOBITS | SHF_ALLOC + SHF_WRITE | Uninitialized globals (zeroed) |
| .symtab | SHT_SYMTAB | none | All symbols (for debugging) |
| .strtab | SHT_STRTAB | none | Symbol names |
| .dynsym | SHT_DYNSYM | SHF_ALLOC | Dynamic symbols (for linking) |
| .dynstr | SHT_STRTAB | SHF_ALLOC | Dynamic symbol names |
| .rela.text | SHT_RELA | SHF_ALLOC | Code relocations |
| .init | SHT_PROGBITS | SHF_ALLOC + SHF_EXECINSTR | Initialization code |
| .fini | SHT_PROGBITS | SHF_ALLOC + SHF_EXECINSTR | Cleanup code |
Note: .bss has type SHT_NOBITS — it takes no space in the file but is zero-filled in memory.
$ readelf -S /bin/ls | grep -E '\.(text|data|bss|rodata|symtab|strtab)' [11] .text PROGBITS 0000000000001000 00001000 001a52 [17] .rodata PROGBITS 0000000000003000 00003000 000ba0 [24] .data PROGBITS 0000000000004dc0 00003dc0 000020 [25] .bss NOBITS 0000000000004de0 00003de0 000040 [26] .symtab SYMTAB 0000000000000000 00003e28 000c90
Why does .bss have type SHT_NOBITS?
If .rodata and .text are in the same PT_LOAD segment, what permissions does the kernel set for .rodata?
You now know the common sections. But remember — sections and segments are different things. The next concept clarifies this distinction.
Match each section type to its correct description: