ELF64文本/数据段布局与填充机制解析及编译程序段结构相关疑问
Let's break down your questions step by step using the readelf output you shared.
First, your initial guesses are spot-on:
- The
LOADsegment starting at0x401000(withR Eflags) is indeed the text segment (holds executable machine code). - The
LOADsegment starting at0x403e00(withRWflags) is the data/bss segment—notice thatMemSiz(0x238) is larger thanFileSiz(0x230), which accounts for the uninitializedbsssection that gets allocated in memory but isn't stored in the executable file itself.
1. What are the other two read-only LOAD segments used for?
Looking at your output, there are two additional R-only LOAD segments, each serving a distinct purpose:
a. First LOAD segment (0x400000)
This segment contains critical ELF metadata and helper sections needed to load and initialize the program:
- It includes the
PHDR(program headers) andINTERP(path to the dynamic linker,/lib64/ld-linux-x86-64.so.2in your case) sections. - It also covers the ELF file header itself, plus read-only note sections (like
.note.gnu.build-id) that carry build or version information.
All of this data is marked read-only because it never needs modification during program execution.
b. Third LOAD segment (0x402000)
This segment holds read-only data sections that don't require execution permissions:
- The
.rodatasection (string constants like your"hello world"text,constglobal variables). - Dynamic linking-related read-only sections like
.dynsym(dynamic symbol table),.dynstr(dynamic symbol strings), and.hash(symbol hash table).
Separating these from the executable text segment is a security choice—making read-only data non-executable reduces the attack surface for code injection attacks.
2. How does ELF padding work?
ELF padding exists to satisfy alignment requirements defined by the Align field in program headers (here, 0x1000 = 4KB, the default page size for x86-64). Here's the core mechanism:
- Each
LOADsegment's virtual address (VirtAddr) must be a multiple ofAlign. - The segment's offset in the executable file (
Offset) must also satisfyVirtAddr % Align == Offset % Align—this ensures the segment maps cleanly to memory pages without misalignment. - When a section's actual size doesn't fill an entire alignment block, padding is added (either in the file or in memory) to reach the next alignment boundary.
The 2MB padding you read about refers to huge page alignment, a performance optimization for large memory regions. By default, GCC uses 4KB pages, so you won't see 2MB-aligned padding unless you explicitly enable huge pages with flags like -mcmodel=large or linker options like -Wl,-z,huge-page-size=2MB.
3. Why doesn't the data segment start at 0x403000?
Your expectation that the data segment should start at 0x403000 (the next 4KB boundary after the third LOAD segment) makes sense in theory, but linker behavior and dynamic linking rules change the layout:
- The third
LOADsegment ends at0x402000 + 0x138 = 0x402138. The next 4KB boundary is0x403000, but the linker didn't place the data segment here because:- File offset alignment: The data section in your executable starts at file offset
0x2e00. To meet the ELF ruleVirtAddr % Align == Offset % Align, the virtual address for the data segment must be0x403e00(since0x403e00 % 0x1000 = 0xe00, matching0x2e00 % 0x1000 = 0xe00). - Permission separation: Linkers intentionally place read-only and read-write segments on separate page boundaries to enforce strict memory protection. The gap between
0x402fffand0x403dffis unused padding memory—trying to access this region would trigger a segmentation fault, adding an extra layer of security against memory corruption attacks.
- File offset alignment: The data section in your executable starts at file offset
内容的提问来源于stack exchange,提问作者Peter

