Understanding Linux Executables from First Principles
Executable and Linking Format (ELF) files form the structural foundation of nearly every program executed on a modern Linux system.
Everything that eventually reaches the processor originates as a precisely defined arrangement of bytes inside an ELF file, including:
- Executable machine code
- Data sections
- Symbol tables
- String tables
- Relocation records
- Dynamic-loader metadata
- Memory-segment layouts
- Runtime descriptors
- Debugging and linking information
Despite ELF's central role, most developers interact with it only indirectly through compilers, linkers, loaders, debuggers, and diagnostic tools. They rarely inspect executable files directly or verify their structures at the binary level.
This book removes that abstraction layer.
Purpose of This Book
ELF and Binary Inspection teaches readers how to interpret executable files with the same precision used by:
- The Linux kernel loader
- The dynamic linker
- Linkers and binary utilities
- Debuggers
- Low-level system tools
Rather than depending on external utilities or library-assisted parsers, this book treats ELF strictly as a binary structure governed by the System V ABI.
Every field, offset, size, boundary, and relationship is:
- Read explicitly
- Validated manually
- Interpreted deterministically
- Checked against the actual file size
- Understood at the byte level
There is no automated decoding, opaque helper code, standard library, or hidden parsing framework.
The objective is not merely to display ELF information, but to understand exactly how that information is stored, located, validated, and interpreted.
Implementation Approach
The examples use:
- Intel-syntax x86-64 assembly
- Raw Linux system calls
- Direct file access
- Explicit pointer arithmetic
- Manual binary validation
- No C standard library
- No external parsing libraries
- No runtime abstraction layer
This minimal environment forces every operation to remain visible and understandable.
The reader learns how data moves from a file on disk into memory, how structures are addressed, how boundaries are checked, and how individual bytes acquire meaning under the ELF specification.
What the Reader Will Learn
Through practical implementations, the reader learns to:
- Read ELF executable headers without
libc - Validate ELF magic bytes, class, encoding, and version
- Interpret file type, architecture, and entry-point information
- Traverse program-header tables
- Traverse section-header tables
- Use explicit pointer and offset arithmetic
- Decode section-name and symbol-name string tables
- Interpret symbol-table entries
- Examine relocation records
- Read dynamic-linking metadata
- Validate offsets and sizes against file boundaries
- Detect malformed or inconsistent structures
- Construct small and deterministic binary-inspection tools
- Understand ELF as a structured byte stream rather than abstract metadata
Progressive Exploration of ELF
Each chapter introduces a deeper layer of ELF architecture.
The discussion begins with the fundamental file header and progressively expands into the structures used by loaders, linkers, debuggers, and runtime systems.
Topics include:
- ELF identification and file headers
- Program-header tables
- Section-header tables
- String tables
- Symbol tables
- Relocation records
- Dynamic-linking structures
- File-offset and memory-address relationships
- Boundary and integrity validation
- Construction of standalone inspection tools
This progression allows readers to move from recognizing an ELF file to reconstructing the logic required to inspect and validate it independently.
ELF Headers
The ELF header is the entry point to the entire file.
It describes essential properties such as:
- ELF class
- Byte order
- ABI identification
- Object-file type
- Target architecture
- Entry-point address
- Program-header location
- Section-header location
- Header sizes
- Table-entry sizes
- Number of table entries
- Section-name string-table index
The book explains how to read each field directly from its binary location and how to verify that its value is valid before using it.
Rather than trusting offsets blindly, every reference is checked against the actual mapped or loaded file size.
Program Headers
Program headers describe how an executable or shared object should be mapped into memory.
They are especially important to the Linux kernel loader and dynamic linker.
The reader learns how program-header entries define:
- Loadable memory segments
- File offsets
- Virtual addresses
- Physical-address fields
- File-backed sizes
- In-memory sizes
- Access permissions
- Alignment requirements
- Dynamic-linking information
- Interpreter paths
- Thread-local storage
- Stack properties
- Read-only relocation regions
This section establishes the distinction between the file's physical organization and its runtime memory image.
Section Headers
Section headers describe the logical organization of an ELF file for linking, relocation, debugging, and analysis.
The reader learns how to locate and interpret sections containing:
- Executable code
- Initialized data
- Uninitialized data
- Read-only constants
- Symbol tables
- String tables
- Relocation entries
- Dynamic-linking records
- Debugging information
- Notes and metadata
The book also explains why sections and segments serve different purposes.
Sections primarily support link-time and inspection operations, while segments primarily describe runtime memory mapping.
String Tables
ELF files store many names inside string tables rather than embedding them directly in other structures.
These names may identify:
- Sections
- Symbols
- Shared libraries
- Dynamic-linking entries
- Version records
The reader learns how offsets inside ELF structures refer to null-terminated strings within a string table.
Every string reference must be validated carefully to ensure that:
- The offset is inside the table
- The referenced string is properly terminated
- The read operation does not cross the file boundary
- Malformed data cannot cause uncontrolled memory access
Symbol Tables
Symbol tables connect names with code, data, addresses, sizes, bindings, visibility rules, and section relationships.
The book explains how to decode symbol records and interpret:
- Symbol-name offsets
- Symbol type
- Symbol binding
- Symbol visibility
- Section index
- Symbol value
- Symbol size
- Defined and undefined symbols
- Local and global symbols
- Weak symbols
- Function and object symbols
By decoding symbols manually, the reader gains a clearer understanding of how assemblers, compilers, linkers, loaders, and debuggers communicate.
Relocation Records
Relocation records describe locations that must be adjusted when code and data are linked or loaded.
They are essential because object files often contain references whose final addresses are not yet known.
The reader learns how relocation entries connect:
- A location requiring modification
- A relocation type
- A referenced symbol
- An optional addend
- The calculation used to produce the final value
This reveals how separate object modules become a coherent executable or shared library.
Dynamic-Linking Metadata
Dynamically linked ELF files contain structures used by the runtime loader to locate libraries, resolve symbols, apply relocations, and prepare the process for execution.
The book examines metadata related to:
- Required shared libraries
- Dynamic symbol tables
- Dynamic string tables
- Relocation tables
- Procedure Linkage Table entries
- Global Offset Table entries
- Initialization routines
- Finalization routines
- Runtime search paths
- Symbol hashing
- Version information
These structures are inspected directly rather than through commands such as readelf, objdump, or nm.
Strict Boundary Validation
Binary parsing is unsafe unless every offset and size is validated before use.
A field may appear structurally correct while still pointing outside the file or causing an arithmetic overflow.
For this reason, the book emphasizes strict validation of:
- Header sizes
- Table offsets
- Entry sizes
- Entry counts
- Section boundaries
- Segment boundaries
- String-table references
- Symbol-table references
- Relocation-table references
- Offset-plus-size calculations
- Multiplication used to calculate table lengths
The reader learns to reject malformed input before dereferencing pointers or reading memory.
This approach mirrors the defensive checks required in kernels, loaders, security tools, and production-quality binary parsers.
Practical Inspection Utilities
The volume culminates in the construction of complete inspection utilities implemented entirely in x86-64 assembly.
These projects include:
- A hexadecimal file viewer
- An ELF header inspector
- A section-information printer
- A string-table reader
- A symbol-table inspector
- A fully standalone ELF section viewer
Each utility communicates with Linux using raw system calls and performs its own file reading, memory handling, formatting, traversal, and validation.
No external runtime or standard library is required.
Why the Minimalist Approach Matters
The deliberate minimalism of this book serves a clear purpose: it enforces absolute clarity about how binaries are structured, validated, loaded, and executed.
High-level libraries can parse ELF files quickly, but they often hide the details that matter most to low-level programmers.
When parsing is performed manually, the reader must understand:
- Where every structure begins
- How every field is encoded
- Which fields refer to other structures
- How offsets are converted into addresses
- Which sizes are trusted
- Which boundaries must be checked
- How malformed data should be rejected
- How the operating system interprets the final file
True understanding does not come from reading the output of existing tools. It emerges from reconstructing those tools from first principles.
From File Bytes to Process Memory
An ELF file is not merely a static container.
It defines the transition from bytes stored on disk to instructions and data active in process memory.
Understanding this transition requires examining:
- File offsets
- Virtual addresses
- Segment alignment
- Memory permissions
- Zero-filled regions
- Entry points
- Runtime relocation
- Dynamic symbol resolution
- Loader responsibilities
By studying these relationships directly, the reader understands how executable code moves from storage into execution.
Relationship to Earlier Volumes
This book builds upon foundations established in earlier volumes covering:
- Computer architecture
- x86-64 assembly language
- Linux system calls
- Process memory
- Memory management
- Calling conventions
- Binary data representation
It forms a critical link in the broader study of low-level systems programming.
Assembly language explains how the processor executes instructions. System calls explain how programs communicate with the kernel. Memory management explains how data is represented and accessed at runtime.
ELF explains how those instructions and data are packaged, described, linked, loaded, and prepared for execution.
Why ELF Mastery Matters
A practical understanding of ELF is essential for many advanced fields, including:
- Systems programming
- Compiler construction
- Assembler development
- Linker development
- Loader implementation
- Debugger development
- Reverse engineering
- Malware analysis
- Security research
- Binary instrumentation
- Operating-system development
- Runtime development
- Embedded and specialized systems
- Performance analysis
- Executable-file validation
ELF knowledge allows developers to reason about compiled software at the same level as the system components responsible for loading and executing it.
Beyond the ELF Specification
The aim of this book is not to reproduce the ELF specification as a passive reference document.
Specifications describe structures and rules, but practical understanding requires implementing those rules.
This volume therefore guides the reader in writing real, executable, low-level tools that inspect and validate ELF binaries with strictness comparable to the Linux kernel and runtime loader.
Through implementation, ELF ceases to be theoretical metadata and becomes tangible:
A sequence of bytes that can be located, read, validated, interpreted, and understood without depending on a library or abstraction layer.
Final Objective
By reaching the point where an ELF executable can be parsed, validated, and inspected using only a few hundred lines of assembly, the reader gains the ability to reason about compiled programs at their most fundamental level.
By the end of this volume, the reader will understand:
- Every major ELF structure
- The purpose of each important field
- How tables and sections are located
- How symbols and strings are connected
- How relocations modify code and data
- How dynamic linking is described
- How loaders map files into memory
- How file boundaries must be validated
- How malformed binaries are detected
- How standalone inspection tools are constructed
The reader will possess the practical expertise required to inspect ELF binaries directly, with full awareness of the fields, offsets, invariants, relationships, and constraints that govern executable code on modern Linux systems.