Back to articles
Binary Analysis

Demystifying the ELF Binary Format

A comprehensive guide to understanding ELF headers, sections, and the linking process.

kos1
January 9, 2026
11 min read

Introduction

ELF (Executable and Linkable Format) is the standard binary format on Linux and most Unix systems. Understanding ELF is essential for reverse engineering, malware analysis, and low-level debugging.

ELF Structure Overview

Every ELF file consists of:

  • ELF Header - File metadata and entry point
  • Program Headers - Describe segments for loading
  • Section Headers - Describe sections for linking
  • Data - Code, data, symbols, relocations, etc.

The ELF Header

The first 64 bytes (for ELF64) contain critical information:

typedef struct {
    unsigned char e_ident[16];  // Magic number and info
    uint16_t e_type;            // Object file type
    uint16_t e_machine;         // Architecture
    uint32_t e_version;         // Object file version
    uint64_t e_entry;           // Entry point address
    uint64_t e_phoff;           // Program header offset
    uint64_t e_shoff;           // Section header offset
    uint32_t e_flags;           // Processor flags
    uint16_t e_ehsize;          // ELF header size
    uint16_t e_phentsize;       // Program header entry size
    uint16_t e_phnum;           // Number of program headers
    uint16_t e_shentsize;       // Section header entry size
    uint16_t e_shnum;           // Number of section headers
    uint16_t e_shstrndx;        // Section name string table index
} Elf64_Ehdr;

The Magic Number

$ hexdump -C /bin/ls | head -1
00000000  7f 45 4c 46 02 01 01 00  00 00 00 00 00 00 00 00

7f 45 4c 46 = "\x7fELF" - The magic number
02 = 64-bit (01 = 32-bit)
01 = Little endian (02 = big endian)
01 = ELF version

Program Headers (Segments)

Program headers tell the loader how to create the process image:

$ readelf -l /bin/ls

Program Headers:
  Type           Offset   VirtAddr           FileSiz  MemSiz   Flg
  PHDR           0x000040 0x0000000000400040 0x0002d8 0x0002d8 R  
  INTERP         0x000318 0x0000000000400318 0x00001c 0x00001c R  
  LOAD           0x000000 0x0000000000400000 0x019d14 0x019d14 R E   <- Code
  LOAD           0x01a000 0x000000000061a000 0x001a50 0x003b30 RW    <- Data
  DYNAMIC        0x01a570 0x000000000061a570 0x0001f0 0x0001f0 RW 
  GNU_STACK      0x000000 0x0000000000000000 0x000000 0x000000 RW    <- Stack perms

Section Headers

Sections organize the binary for linking and debugging:

$ readelf -S /bin/ls

Section Headers:
  [Nr] Name              Type             Address           Size
  [ 1] .interp           PROGBITS         0x0000000000400318 0x1c
  [ 2] .text             PROGBITS         0x0000000000401000 0x15a4a  <- Code
  [ 3] .rodata           PROGBITS         0x0000000000417000 0x4d14   <- Read-only data
  [12] .data             PROGBITS         0x000000000061a000 0x0270   <- Initialized data
  [13] .bss              NOBITS           0x000000000061c000 0x2100   <- Uninitialized data
  [14] .symtab           SYMTAB           0x0000000000000000 0x3a80   <- Symbols
  [15] .strtab           STRTAB           0x0000000000000000 0x1e20   <- String table

Parsing ELF in Code

#include 
#include 
#include 

void parse_elf(const char *path) {
    int fd = open(path, O_RDONLY);
    struct stat st;
    fstat(fd, &st);
    
    void *map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
    Elf64_Ehdr *ehdr = (Elf64_Ehdr *)map;
    
    // Verify magic
    if (memcmp(ehdr->e_ident, "\x7fELF", 4) != 0) {
        printf("Not an ELF file\n");
        return;
    }
    
    printf("Entry point: 0x%lx\n", ehdr->e_entry);
    printf("Sections: %d\n", ehdr->e_shnum);
    
    // Iterate sections
    Elf64_Shdr *shdr = (Elf64_Shdr *)(map + ehdr->e_shoff);
    char *strtab = (char *)(map + shdr[ehdr->e_shstrndx].sh_offset);
    
    for (int i = 0; i < ehdr->e_shnum; i++) {
        printf("Section: %s\n", strtab + shdr[i].sh_name);
    }
}

Useful Tools

  • readelf - Display ELF information
  • objdump - Disassemble and display object info
  • nm - List symbols
  • patchelf - Modify ELF binaries

Conclusion

ELF is the foundation of executable formats on Linux. Understanding its structure opens doors to debugging, security research, and systems programming.