ELF Format
The container for Linux executables and libraries. Its header tells you the CPU, endianness, and linking, everything you need before opening a disassembler.
ELF (Executable and Linkable Format) is the standard container for executables, shared libraries, and object files on Linux and most embedded Unix systems. The binaries you pull out of a rootfs are almost always ELF.
What it is
An ELF file wraps compiled code and data in a self-describing container. It starts with the magic \x7fELF, followed by a header that records the class (32- or 64-bit), the endianness, the target machine (ARM, MIPS, Xtensa, RISC-V, x86), the entry point, and the offsets of two tables. Those two tables give two views of the same bytes: the program header table describes loadable segments for the operating system, and the section header table describes named sections (.text, .rodata, .data, .bss, .symtab) for linkers and analysis tools.
Why it matters
Before you can reverse a binary you have to load it correctly, and the header tells you how. The machine field selects the right Ghidra or IDA processor; the class and endianness set the disassembly mode; the dynamic section lists the shared libraries the binary needs, which hints at its capabilities. Get these wrong and the disassembler produces nonsense. Reading the header first turns a mystery blob into a target you can set up in seconds.
How it works
At load time the kernel and dynamic linker read the program headers and map each PT_LOAD segment into memory with the right permissions: .text read-execute, .rodata read-only, .data/.bss read-write. Section headers are not needed to run the program at all; they exist for tooling, which is why they can be stripped. The symbol table (.symtab) and string table (.strtab) map addresses to function and variable names, and the dynamic symbol table (.dynsym) lists imported and exported symbols for linking.
How to inspect it
file and readelf answer the setup questions immediately.
file ./binary # arch, endianness, static vs dynamic, stripped?
readelf -h ./binary # full header: class, machine, entry point
readelf -d ./binary # dynamic section: NEEDED shared libraries
readelf -S ./binary # section headers: .text, .rodata, .symtab
nm ./binary # symbol table (empty if stripped)
strings -a ./binary # embedded strings: URLs, format specifiers, secrets
file in one line tells you the CPU and whether the binary is stripped; readelf -h and -d give the architecture and the linked libraries so you can pick the matching processor. .rodata and strings surface hardcoded URLs, error messages, and sometimes credentials that shortcut the whole analysis. Any surviving symbols make the disassembly dramatically easier to read.
Pitfalls
- Stripped binaries drop
.symtaband often the section headers; the program headers still tell you where code loads, so reverse from the entry point and imports. - The
.bsssection occupies no file bytes (it is zero-filled at load), so its size in the file is zero even though it uses memory at runtime. - Do not assume x86. Embedded ELF is frequently big-endian MIPS or Xtensa; always confirm the machine field before loading.
- A valid ELF header can be faked or corrupted. If tools disagree, trust
readelfon the raw bytes over a file extension.