Appendix C: ELF Format Quick Reference

ELF Header Fields

Every ELF file starts with a 64-byte header (for 64-bit) at offset 0.

+----------------+--------+------------------------------------------------+
| Field          | Size   | Description                                    |
+----------------+--------+------------------------------------------------+
| e_ident[0..3]  | 4      | Magic: 0x7f 'E' 'L' 'F'                       |
| e_ident[4]     | 1      | Class: 1 = 32-bit, 2 = 64-bit                 |
| e_ident[5]     | 1      | Data: 1 = little-endian, 2 = big-endian        |
| e_ident[6]     | 1      | Version: 1 (always)                            |
| e_ident[7]     | 1      | OS/ABI: 0 = SYSV, 3 = Linux                   |
| e_ident[8..15] | 8      | Padding (zeroes)                               |
+----------------+--------+------------------------------------------------+
| e_type         | 2      | ET_REL (1) = relocatable (.o)                  |
|                |        | ET_EXEC (2) = executable (fixed address)       |
|                |        | ET_DYN (3) = shared object / PIE executable    |
+----------------+--------+------------------------------------------------+
| e_machine      | 2      | EM_X86_64 (0x3E), EM_AARCH64 (0xB7), etc.     |
| e_version      | 4      | 1 (current)                                    |
| e_entry        | 8      | Entry point virtual address (_start)           |
| e_phoff        | 8      | Program header table offset in file            |
| e_shoff        | 8      | Section header table offset in file            |
| e_flags        | 4      | Processor-specific flags                       |
| e_ehsize       | 2      | ELF header size (64 for 64-bit)                |
| e_phentsize    | 2      | Size of one program header entry (56)          |
| e_phnum        | 2      | Number of program header entries                |
| e_shentsize    | 2      | Size of one section header entry (64)          |
| e_shnum        | 2      | Number of section header entries                |
| e_shstrndx     | 2      | Index of section name string table              |
+----------------+--------+------------------------------------------------+

Common Sections

+-------------+-----------------------------------------------------------+
| Section     | Contents                                                  |
+-------------+-----------------------------------------------------------+
| .text       | Executable machine code                                   |
| .data       | Initialized global/static variables                       |
| .bss        | Uninitialized globals (zero-filled at load, no file space)|
| .rodata     | Read-only data (string literals, constants)               |
| .symtab     | Symbol table (functions, globals) — for linking/debugging |
| .strtab     | String table for symbol names                             |
| .shstrtab   | String table for section names                            |
| .rel / .rela| Relocation entries (fixups for the linker)                |
| .plt        | Procedure Linkage Table (lazy binding stubs)              |
| .got        | Global Offset Table (resolved dynamic addresses)         |
| .got.plt    | GOT entries specifically for PLT                         |
| .dynamic    | Dynamic linking info (needed libraries, symbol tables)    |
| .interp     | Path to dynamic linker (/lib64/ld-linux-x86-64.so.2)    |
| .init/.fini | Constructor/destructor code                              |
| .debug_*    | DWARF debug information (line numbers, types, variables)  |
| .eh_frame   | Exception/stack unwinding tables                         |
| .note.*     | Build ID, ABI tags                                       |
| .comment    | Compiler version string                                   |
+-------------+-----------------------------------------------------------+

Common Segment Types (Program Headers)

Segments are the runtime view. The kernel reads these to load the program.

+----------------+----------------------------------------------------------+
| Type           | Purpose                                                  |
+----------------+----------------------------------------------------------+
| PT_LOAD        | Loadable segment. Mapped into memory. Usually two:       |
|                |   1) r-x: .text, .rodata (code + constants)              |
|                |   2) rw-: .data, .bss (writable data)                    |
+----------------+----------------------------------------------------------+
| PT_DYNAMIC     | Points to .dynamic section. Used by the dynamic linker   |
|                | to find shared libraries and resolve symbols.            |
+----------------+----------------------------------------------------------+
| PT_INTERP      | Path to the dynamic linker (e.g., /lib64/ld-linux...)    |
|                | Kernel reads this to know which loader to invoke.        |
+----------------+----------------------------------------------------------+
| PT_NOTE        | Auxiliary info: build ID, ABI version.                   |
|                | Not loaded into process memory.                          |
+----------------+----------------------------------------------------------+
| PT_GNU_STACK   | Stack executability. If absent or flags=RW, stack is NX.  |
|                | If flags=RWX, stack is executable (rare, insecure).       |
+----------------+----------------------------------------------------------+
| PT_GNU_RELRO   | Read-only after relocation. The dynamic linker resolves   |
|                | GOT entries, then marks this region read-only.           |
+----------------+----------------------------------------------------------+
| PT_PHDR        | Points to the program header table itself.               |
+----------------+----------------------------------------------------------+

Quick Inspection Commands

readelf -h binary      # ELF header
readelf -S binary      # Section headers
readelf -l binary      # Program headers (segments)
readelf -s binary      # Symbol table
readelf -d binary      # Dynamic section
objdump -d binary      # Disassemble .text
hexdump -C binary | head  # Raw bytes (look for 7f 45 4c 46)