Executable File Formats: ELF, PE/COFF and Mach-O

How executables, libraries and object files are laid out on disk and mapped in memory. Each page walks through the structures with their offsets and annotates every byte of a small working program: hover the hex dump to see which field a byte belongs to.

Side by side

ELFPE/COFFMach-O
Used byLinux, BSDs, Android, Solaris, firmware, game consolesWindows, UEFI, Xbox, .NET assembliesmacOS, iOS, iPadOS, watchOS, tvOS, visionOS
Magic (first bytes)7f 45 4c 46 "\x7fELF"4d 5a "MZ", then "PE\0\0" at e_lfanewcf fa ed fe (64-bit LE), ca fe ba be (universal)
32 / 64-bitEI_CLASS in e_identOptional header Magic: 0x10b / 0x20bMH_MAGIC / MH_MAGIC_64
EndiannessEI_DATA: eitherAlways little endianMagic byte order; fat headers and code signatures are big endian
Multi-architecture fileNoARM64X only (ARM64 + ARM64EC)Universal binaries (fat_header)
Main headerElf64_Ehdr, 64 bytesDOS header + IMAGE_FILE_HEADER + IMAGE_OPTIONAL_HEADER64mach_header_64, 32 bytes
How the loader maps itProgram headers (PT_LOAD segments)Section tableLC_SEGMENT_64 load commands
Linker / tool viewSection headers (optional at run time)Same section tableSections inside segment commands
Entry pointe_entry (VA)AddressOfEntryPoint (RVA)LC_MAIN entryoff (file offset), or LC_UNIXTHREAD
Address units in headersVirtual addresses and file offsetsRVAs (relative to ImageBase)VM addresses and file offsets
Libraries neededDT_NEEDED in .dynamicImport directory, one descriptor per DLLLC_LOAD_DYLIB, referenced by ordinal
Symbol bindingFlat namespace: any library can provide a symbolTwo-level: each import names its DLLTwo-level: each import names its library ordinal
Imported function callsPLT stub → .got.plt, lazy by defaultcall [IAT], resolved at load timeStub in __stubs → __got, bound at load time
Run-time loaderPT_INTERP, e.g. /lib64/ld-linux-x86-64.so.2ntdll.dll (Ldr*)LC_LOAD_DYLINKER: /usr/lib/dyld
Relocations at load.rela.dyn (R_*_RELATIVE, GLOB_DAT...).reloc base relocations (delta only)Chained fixups, in place
Exports.dynsym + .gnu.hashExport directory (names, ordinals)Exports trie
Symbols.symtab / .dynsym (Elf64_Sym)None in images (debug info in a PDB)LC_SYMTAB (nlist_64)
Thread-local storagePT_TLSTLS directory (+ callbacks)__thread_vars descriptors
Constructors.init_arrayCRT tables; TLS callbacks run first__mod_init_func / __init_offsets
Code signatureNone in the formatAuthenticode in the Security directory (optional)LC_CODE_SIGNATURE, mandatory on arm64
Debug informationDWARF in .debug_* or a separate file (build ID)PDB file (path and GUID in the debug directory)DWARF in a .dSYM bundle (matched by UUID)
Object filesELF ET_RELCOFF (.obj)Mach-O MH_OBJECT

Identify a file by its first bytes

BytesASCIIFormat
7f 45 4c 46\x7fELFELF
4d 5aMZDOS / PE (check e_lfanew → "PE\0\0")
cf fa ed feMach-O 64-bit, little endian
ce fa ed feMach-O 32-bit, little endian
ca fe ba beUniversal Mach-O (or a Java class file)
ca fe ba bfUniversal Mach-O with 64-bit offsets
64 86 / 4c 01 / 64 aaCOFF object (Machine field first: AMD64, I386, ARM64)
21 3c 61 72 63 68 3e 0a!<arch>\nStatic library (.a, .lib)
64 65 78 0adex\nAndroid DEX
00 61 73 6d\0asmWebAssembly