The Executable and Linkable Format (ELF) is the standard object-file format on Linux and other UNIX-derived systems. An object file here is the binary representation intended to be executed directly on a processor — it comes in three types: relocatable (code and data to be combined later by the linker), executable (a ready-to-run program), and shared object (a library that can be linked or dynamically loaded).
Structure
What an ELF file is composed of
Any ELF file is built from four parts, in order:
The ELF header at the top describes the file as a whole; the program header table tells the loader how to build a process image; the section sequence is what the linker consumes; and the section header table indexes those sections.
Relevant Sections
The sections a binary analyst looks at
Section
Holds
.text
the executable instructions of the program
.bss
uninitialised data that contributes to the program’s memory image
.data
initialised writable data that contributes to the memory image
.rodata
initialised read-only data
.symtab
the program’s symbol table
.plt
Procedure Linkage Table — entry points for calling functions from shared libraries
.got
Global Offset Table — holds the resolved addresses the PLT looks up at runtime
Permissions
Each section is mapped with its own protection bits
Sections are loaded into separate memory pages with different permissions — Read, Write, eXecute — so a page that needs just one of those is not given the others. .text holds code and must be executable; .bss holds mutable uninitialised data and must be readable and writable but not executable; the ELF header itself should be neither writable nor executable. Not all memory regions in a process are executable — running code from an unintended section is what a segmentation fault reports when a buffer overflow overwrites the saved return address with garbage.
Example
Where a few C declarations land
const float pi = 3.14159; // constant --> .rodatachar name[20]; // uninitialised array --> .bsschar default_name[] = "Alice"; // initialised array --> .data // the literal "Name: " used by scanf --> .rodata
Constants go to .rodata (read-only), uninitialised globals to .bss, initialised mutable globals to .data. Matching a declaration to its section is the first static-analysis step before reading any disassembly.