Sections are the linker's view of a binary. Each section has a specific purpose: holding executable code, data, symbol information, or strings. Let's explore the most important ones.
.text — Executable Code
The .text section contains the compiled machine instructions of your program. This is where your functions live after compilation. The section is marked executable (X) and allocatable (A), but never writable — modifying code at runtime would be a security risk.
.data / .bss — Program Data
Global and static variables land in one of two sections depending on their initialization state:
.data
Initialized data. Variables like int count = 42; occupy space both on disk and in memory. Writable at runtime.
.bss
Uninitialized data. Variables like int buffer[4096]; take zero bytes on disk but expand to full size in memory.
.rodata — Read-Only Data
String literals, constant arrays, and other immutable data live in .rodata. When you write printf("Hello, world!\n"), the string "Hello, world!\n" is stored here. This section is mapped read-only in memory — any attempt to modify it triggers a segmentation fault.
.symtab / .strtab — Symbol and String Tables
The symbol table (.symtab) is the binary's phone book. Each entry maps a name to an address, size, and type. Function names, global variables, and section references all appear here. The actual name strings are stored separately in .strtab, with st_name being an offset into that string table.
.shstrtab — Section Header String Table
This special string table holds the names of sections themselves. When the sh_name field in a section header says 27, it means "go to offset 27 in .shstrtab and read the null-terminated string there." The ELF header's e_shstrndx field tells you which section index is .shstrtab.
Section Table
Which section type has SHT_NOBITS?