The Container Format
When you run a program, you might imagine the computer is simply reading a long list of CPU instructions from a file and executing them one by one. That mental model is close, but incomplete. A compiled program is not a raw stream of machine code — it is a structured document that tells the operating system how to set up the environment before a single instruction runs.
On Linux, this structure is defined by the ELF (Executable and Linkable Format) standard. ELF is the universal container format for executables, shared libraries (.so files), and object files (.o files) on Linux and most Unix-like systems. macOS uses a similar format called Mach-O; Windows uses PE (Portable Executable). The concepts are the same across all three.
Think of an ELF file like a shipping container with a manifest on the door. The manifest (the ELF header) tells the OS: "I am a 64-bit application, my entry point is at address 0x401000, and I depend on the C standard library." The cargo inside (the sections) is compartmentalized — code lives separately from data, read-only constants live separately from mutable globals. This compartmentalization is not just organizational tidiness; it is the foundation of memory protection, security, and efficient loading.
When you type ./myprogram, the kernel reads the ELF header first. It uses that information to set up virtual memory mappings, locate required libraries, and hand control to the loader — all before your main() function is called.
Key Point: The distinction between "a binary" and "machine code" matters in practice. Tools like file, readelf, and objdump let you inspect the ELF structure directly. When a program crashes, misbehaves, or refuses to load, understanding ELF anatomy is often the fastest path to diagnosis.