Understanding readelf --segments

Think of the ELF program (calc) as a building being handed to the operating system.
Image:AI Generated

The OS loader needs a map to know:

  • which parts of the file to load into memory
  • what permissions each part needs (read/write/execute)
  • where code lives
  • where data lives
  • where the dynamic linker is
  • what areas must be protected

This “map” is made of Program Headers, also called segments.

Sections (like .text, .data) are for humans and compilers.
Segments are for the loader — they actually get loaded into memory.

Now let’s decode your output.

Top-Level Info

Elf file type is DYN
Entry point 0x1050
There are 13 program headers

What this means:

  • DYN = Position-Independent Executable (modern GCC default for security)
  • Entry point 0x1050 = where the CPU jumps to start execution
  • 13 program headers = 13 “segments” the loader must handle

Walk Through Each Segment

Let's explore them in the order shown and tie them to what they do.


Segment 0 — PHDR (Program Header Table itself)

PHDR  Offset 0x40   R

Think of this as:

“This segment contains the very instructions that describe all the other segments.”

The loader loads this so it can read the rest of the map.


Segment 1 — INTERP (Interpreter)

INTERP Offset 0x318   R
[Requesting: /lib64/ld-linux-x86-64.so.2]

This tells the OS:

“I’m a dynamically linked program — please load the dynamic linker to help me.”

The interpreter (ld-linux-x86-64.so.2) handles loading shared libraries like libc.


Segment 2 — First LOAD segment (Read-Only Data)

LOAD  R
Contains: .interp .note.* .gnu.hash .dynsym .dynstr .rela.*

This is the "metadata and linking info" area.

Inside are things like:

  • the dynamic symbol table
  • hash tables for symbol lookup
  • relocation entries

Nothing executable here.


Segment 3 — LOAD (Code Segment)

LOAD   R E
Contains: .init .plt .plt.got .text .fini

This is the most exciting one.

This is where your actual machine code (the program logic) lives.
✔ Marked R E = readable + executable (but not writable)

This contains:

  • .text → compiled instructions
  • .plt / .got.plt structures for dynamic function calls (like calling printf)
  • .init / .fini code run before/after main

Think of this as the “engine” of the car.


Segment 4 — LOAD (More Read-Only Data)

LOAD R
Contains: .rodata .eh_frame_hdr .eh_frame

This is the read-only data region.

  • .rodata constants (like constant strings)
  • .eh_frame exception/unwinding info

Nothing here should be modified at runtime.


Segment 5 — LOAD (Writable Data)

LOAD RW
Contains: .init_array .fini_array .dynamic .got .got.plt .data .bss

This is the data segment.

Anything that changes during execution lives here:

  • .data initialized global variables
  • .bss uninitialized globals
  • .got & .got.plt runtime addresses of functions & libraries
  • .dynamic dynamic linking information
  • init/fini arrays → lists of constructors/destructors

Memory here is readable + writable.


Segment 6 — DYNAMIC

DYNAMIC RW
Contains: .dynamic

This is a small segment specifically for dynamic linking information:
It contains entries like:

  • which libraries to load
  • where the PLT/GOT are
  • relocation tables

7-9 NOTE and GNU_PROPERTY segments

NOTE / GNU_PROPERTY

These are vendor or OS metadata:

  • build ID
  • ABI tag
  • security properties (e.g., CET, stack protection)

They help tools and the kernel but are not directly “used” by your program.


Segment 10 — GNU_EH_FRAME

GNU_EH_FRAME (R)
Contains: .eh_frame_hdr

This is used by exception handling/unwinding (used by C++ and debugging tools).


Segment 11 — GNU_STACK

GNU_STACK RW

This tells the OS:

“Please create a stack for me which is writable but not executable.”

This is a security feature (prevents stack-based shellcode execution).


Segment 12 — GNU_RELRO

GNU_RELRO R
Contains: .init_array .fini_array .dynamic .got

RELRO = Relocation Read-Only
This means:

  • these areas start writable during relocation
  • after linking finishes, they become read-only

Stops attackers from overwriting GOT entries.


Section-to-Segment Mapping

At the bottom:

Segment Sections...
03: .init .plt .plt.got .text .fini

This is simply saying:

“These sections were merged into the same LOAD segment.”

Because loaders deal in segments, not sections.