Reading — step 1 of 5
Read
~1 min readELF Format
ELF Header
The first 64 bytes of an ELF (for 64-bit) is the header:
struct ElfHeader {
char ident[16]; // magic + class + data + version + ...
uint16_t type; // ET_REL (1), ET_EXEC (2), ET_DYN (3), ET_CORE (4)
uint16_t machine; // EM_X86_64 (62), EM_AARCH64 (183), ...
uint32_t version;
uint64_t entry; // virtual address of entry point
uint64_t phoff; // offset to program header table
uint64_t shoff; // offset to section header table
uint32_t flags;
uint16_t ehsize; // size of this header (52 for 32-bit, 64 for 64)
uint16_t phentsize;
uint16_t phnum;
uint16_t shentsize;
uint16_t shnum;
uint16_t shstrndx; // section header string table index
};
ident breakdown:
- bytes 0-3: magic
\x7fELF. - byte 4: class — 1 = 32-bit, 2 = 64-bit.
- byte 5: data — 1 = little-endian, 2 = big-endian.
- byte 6: version (1).
- byte 7: OS/ABI — 0 = System V, 3 = Linux, 9 = FreeBSD.
- byte 8: ABI version.
- bytes 9-15: padding.
Type:
- ET_REL: relocatable object file (.o).
- ET_EXEC: legacy executable (fixed load addr).
- ET_DYN: shared object OR position-independent executable (PIE).
- ET_CORE: core dump.
Modern Linux: most "executables" are ET_DYN (PIE) for ASLR.
Entry point: where the kernel jumps after loading. For dynamic executables, it's the dynamic linker's entry, not main(). For static: _start.
Section vs program headers:
- Section: how the LINKER sees it (logical, named).
- Program: how the LOADER sees it (physical, mapped).
Static linker uses sections; loader uses program headers.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…