.text:00446510; =============== S U B R O U T I N E ===============
.text:00446510
.text:00446510

The ELF header, byte by byte

.text:00446510; author: Can Ozdemir date: 2026-10-14 size: 0xE70 tags: elf, linux, tutorial
.text:00446510
.text:00446510sub_446510procnear
.text:00446511

Every Linux executable, shared library and object file starts with an ELF header. On 64-bit systems it's exactly 64 bytes. It says what kind of file this is, which CPU it is for, where execution starts, and where the rest of the file's tables are. Let's decode a real one: /bin/ls from Ubuntu 24.04.

.text:00446642

The bytes

.text:0044664E
xxd -l 64 /bin/ls
.text:0044665F
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00  |.ELF............|
00000010  03 00 3e 00 01 00 00 00  30 6d 00 00 00 00 00 00  |..>.....0m......|
00000020  40 00 00 00 00 00 00 00  28 24 02 00 00 00 00 00  |@.......($......|
00000030  00 00 00 00 40 00 38 00  0d 00 40 00 1f 00 1e 00  |....@.8...@.....|
.text:0044679A

Everything after the first 16 bytes is little-endian. If that's new, read the hexdump post first.

.text:004467FB

Bytes 0x00 to 0x0f: e_ident

.text:00446819

The first 16 bytes identify the file and say how to read the rest.

.text:0044685B
  • 7f 45 4c 46: the magic number, 0x7f followed by ELF. The file command checks exactly this.
  • 02: class. 1 = 32-bit, 2 = 64-bit.
  • 01: data encoding. 1 = little-endian, 2 = big-endian.
  • 01: ELF version, always 1.
  • 00: OS/ABI, 0 = System V (the usual value on Linux).
  • The rest are the ABI version and padding.
.text:004469AF

Bytes 0x10 to 0x3f: the fields

.text:004469D0

Each field below is read little-endian.

.text:004469F7
  • 0x10 e_type, 2 bytes: 03 00 = 3 = ET_DYN. That's a shared object, which is also what a position-independent executable (PIE) is. A non-PIE executable would be 2 (ET_EXEC), and an object file .o is 1 (ET_REL).
  • 0x12 e_machine, 2 bytes: 3e 00 = 0x3e = 62 = x86-64. You'd see 0xb7 for AArch64 and 0x03 for 32-bit x86.
  • 0x14 e_version, 4 bytes: 01 00 00 00 = 1.
  • 0x18 e_entry, 8 bytes: 30 6d 00 … = 0x6d30. The address where execution starts. In a PIE it's relative, because the loader picks the real base address at run time.
  • 0x20 e_phoff, 8 bytes: 0x40 = 64. The program headers start right after this header.
  • 0x28 e_shoff, 8 bytes: 28 24 02 00 … = 0x22428 = 140328. The section headers are near the end of the file.
  • 0x30 e_flags, 4 bytes: 0, unused on x86-64.
  • 0x34 e_ehsize: 40 00 = 64, the size of this header.
  • 0x36 e_phentsize: 38 00 = 56 bytes per program header, and 0x38 e_phnum: 0d 00 = 13 of them.
  • 0x3a e_shentsize: 40 00 = 64 bytes per section header, and 0x3c e_shnum: 1f 00 = 31 of them.
  • 0x3e e_shstrndx: 1e 00 = 30. The section that holds the section names.
.text:00446EB2

Check it

.text:00446EBD
readelf -h /bin/ls
.text:00446ECF

readelf agrees with every value: Type DYN, Machine X86-64, Entry point 0x6d30, program headers at 64 with 13 entries of 56 bytes, section headers at 140328 with 31 entries of 64 bytes, string table index 30.

.text:00446FA6

Program headers vs. section headers

.text:00446FCC

The header points to two tables, and it's worth knowing why there are two:

.text:00447016
  • Program headers (segments) are what the loader uses. They say which byte ranges of the file to map into memory, at what address, and with which permissions (read, write, execute).
  • Section headers (.text, .rodata, .data, .bss, …) are for linkers and tools. A program runs fine without them; strip removes symbols, and some packers damage section headers on purpose to confuse analysis tools.
.text:004471BD

That second point is the practical lesson: when a tool chokes on a binary, check the header and the program headers by hand. They are what the kernel actually trusts.

.text:00447263

Try it

.text:0044726C
  • readelf -l /bin/ls: list the segments and their permissions
  • readelf -S /bin/ls: list the sections
  • xxd -s 0x40 -l 56 /bin/ls: the first program header, raw
.text:00447313
.text:00447313retn
.text:00447313sub_446510endp
.text:00447313; © 2026 Can Ozdemir. All rights reserved. terms
.text:00447313
.text:00447314; -------- cross references --------
Output
Loading ozdc.us… 34 functions, 3 data segments.
Type help in the command line below and press Enter.