C Mastery / Executable Formats: ELF, Mach-O, PE
Part 5 — Compilation, Linking, and Building

Executable Formats: ELF, Mach-O, PE

This chapter surveys the three dominant executable/object formats: ELF (Linux/Unix), Mach-O (macOS), and PE (Windows). They all implement the same core concepts — sections, symbols, relocations — with different layouts.

Why This Matters

Executable formats are the concrete representation of everything the compiler and linker produce. Knowing their structure helps you debug link/load issues, inspect binaries, and understand platform differences.

Prerequisites

Core Concept

All three formats share these ideas:

They differ in structure, terminology, and tooling.

Comparison

FeatureELFMach-OPE
PlatformLinux, BSD, UnixmacOS, iOSWindows
Object ext.o.o.obj
Shared lib ext.so.dylib.dll
Executable ext(none)(none).exe
Inspect toolreadelf, objdumpotool, llvm-objdumpdumpbin, llvm-readobj

Examples

Inspecting an ELF binary

readelf -h app      # header
readelf -S app      # sections
readelf -l app      # program headers (segments)
readelf -s app      # symbols

Inspecting a Mach-O binary (macOS)

otool -l app
otool -L app        # linked libraries

Inspecting a PE binary (Windows)

dumpbin /headers app.exe
dumpbin /imports app.exe

How It Works

The linker writes the executable in the platform's format, merging sections into loadable segments, recording the entry point, and (for dynamic executables) listing required shared libraries. The loader reads the header to map segments and begin execution.

Variations

Object vs. executable vs. shared

The same format is used for all three, with different flags and structures (e.g., an executable has an entry point; a shared library has dynamic relocations).

Position independence

PIE/PIC affect which relocations are needed and how the loader can randomize the load address (c.sec.5, c.link.2).

Common Mistakes

Undefined Behavior

or crash at run time, outside ISO C's model.

Portability

Under the Hood

An ELF file has an ELF header, a section header table, and a program header table. Mach-O uses "load commands." PE uses a DOS header, a PE header, and sections. All map code/data to memory and record relocations.

Practical Usage

errors.

Exercises

1. On Linux, compile a program and inspect it with readelf -h -S -l -s. 2. Identify the entry point and the .text/.data sections. 3. Compare the dynamic dependencies (ldd on Linux) with what readelf -d shows.

Deep Challenge

Explain how an ELF executable is loaded: program headers, segment mapping, entry point transfer, and how the dynamic loader resolves shared libraries. Relate this to c.link.3.

References

Verification

verified.`

Progress

Concept checkboxes

Mastery levels

ConceptCurrent level (0–8)Target level
c.build.elf05
c.build.macho04
c.build.pe04