Introduction: The ELF LOAD Segment Mystery

When inspecting a modern Linux ELF binary compiled with the GNU Linker (BFD ld) using readelf -Wl, developers often notice four separate LOAD segments instead of the traditional two or three. Specifically, there are frequently two distinct read-only (R) segments: one at the beginning containing ELF headers and notes, and another containing read-only data like .rodata.

This layout has sparked debate in the developer community. Is splitting the read-only segment a performance optimization based on access frequency, or is it an architectural requirement driven by linker scripts, virtual memory contiguity, and page alignment? In this article, we will debunk the performance myth and explore the exact technical mechanics behind this behavior.

Myth-Busting: Access Frequency vs. Linker Architecture

A common misconception is that BFD-ld separates read-only segments to optimize CPU caching or paging based on how frequently sections (like headers vs. .rodata) are accessed. This claim is incorrect.

The GNU Linker does not perform runtime access-frequency profiling when determining segment layouts. The real reason for the split comes down to two strict constraints in executable design:

  • Virtual Address Contiguity: A single ELF LOAD segment must represent a single, contiguous range of virtual memory with uniform access permissions (Read, Write, Execute).
  • Section Layout Order and Hardening: Default linker scripts place the executable code (RX) segment between the ELF headers (R) and the read-only data (R).

The Technical Core: Virtual Memory Contiguity

To understand why these segments cannot be merged, consider how memory mapping works at the OS level. The kernel maps ELF LOAD segments into virtual memory using mmap() based on their memory protection flags (PROT_READ, PROT_WRITE, PROT_EXEC).

In modern ELF executables using security features like -z separate-code, the typical memory layout order generated by the default linker script looks like this: