SimplifyC++ Article
Object File Generation: ELF Format Deep Dive -> Section Headers: .text, .data, .bss
Object File Generation: ELF Format Deep Dive -> Section Headers: .text, .data, .bss
In the ELF object file format, section headers define the organization of the binary’s data into logically distinct regions. Each section header provides metadata about a region of the file or memory, including its size, location, alignment, and purpose. For assemblers targeting the x86-64 architecture, three standard sections are typically used: .text, .data, and .bss. These correspond respectively to executable code, initialized data, and uninitialized data.
This section discusses their purpose, header structure, and how an assembler generates these headers correctly.
1. Role and Purpose of Standard Sections
.text Section
Contains the machine instructions (i.e., assembled code).
Marked as executable and typically read-only.
The linker will place this section in an executable memory segment during program loading.
.data Section
Contains initialized global and static variables.
It is writable and used for storing variables with known initial values at compile-time.
.bss Section
Represents uninitialized data.
It does not occupy space in the file but will be allocated and zero-initialized in memory at runtime.
Typically used for variables that are declared globally or statically without initialization.
2. Section Header Table Overview
Each ELF file has a section header table that begins at the file offset specified by e_shoff in the ELF header. Every entry in the table is 64 bytes in 64-bit ELF and corresponds to a specific section, including metadata such as name, type, flags, and location.
The relevant C structure is:
typedef struct { uint32_t sh_name; uint32_t sh_type; uint64_t sh_flags; uint64_t sh_addr; uint64_t sh_offset; uint64_t sh_size; uint32_t sh_link; uint32_t sh_info; uint64_t sh_addralign; uint64_t sh_entsize;} Elf64_Shdr;Each field must be filled appropriately by the assembler.
3. .text Section Header
This section is responsible for holding executable machine instructions.
Key Fields:
sh_name: Offset into the section header string table pointing to the name ".text".sh_type:SHT_PROGBITS(value = 1), as it contains actual program data.sh_flags:SHF_ALLOC | SHF_EXECINSTR(values =0x6), making the section allocatable and executable.sh_addr: Set to0in relocatable object files.sh_offset: File offset where the.textsection begins.sh_size: Total size of code in bytes.sh_addralign: Typically16for x86-64 instructions to satisfy CPU alignment requirements.sh_entsize:0unless the section holds fixed-size entries (not the case here).
4. .data Section Header
Holds initialized writable data.
Key Fields:
sh_name: Offset to ".data" in the string table.sh_type:SHT_PROGBITSsh_flags:SHF_ALLOC | SHF_WRITE(values =0x3)sh_addr:0(relocatable object)sh_offset: Offset in the file where.datastarts.sh_size: Number of bytes of initialized data.sh_addralign: Often8for general data alignment.sh_entsize:0
5. .bss Section Header
Holds uninitialized data that will be zero-filled by the loader.
Key Fields:
sh_name: Offset to ".bss" in the string table.sh_type:SHT_NOBITS(value =8)sh_flags:SHF_ALLOC | SHF_WRITEsh_addr:0sh_offset: File offset where the section would begin (not used since no file space is allocated).sh_size: Size of zero-initialized space.sh_addralign: Typically8sh_entsize:0
Although .bss does not occupy file space, it must still be described in the section header table to ensure the linker and loader allocate the correct space in memory.
6. Generating Section Headers in an Assembler
During assembler output generation, the following actions are required:
Emit actual section contents:
Write the
.textand.datacontents to the file buffer first.Keep track of each section's starting file offset and size.
For
.bss, track size only, as no bytes are written.
Construct section name string table:
Contains null-terminated strings like
.text,.data,.bss, etc.The assembler must record the offset of each name for use in
sh_name.
Construct section headers:
Create a section header entry for each section.
Include an additional header for the section string table itself.
Populate each field precisely, using tracked offsets, sizes, and alignment values.
Update ELF Header:
After all section headers are created, update the
e_shoff,e_shnum, ande_shstrndxfields of the ELF header.
7. Order and Indexing
The standard order in the section header table (excluding the null section) typically follows:
.text.data.bss.shstrtab(section string table)
The .shstrtab section must be referenced via e_shstrndx in the ELF header to link section names.
Indexing begins from 1, as the first entry (index 0) is reserved and must be all zero bytes to represent the undefined section.
8. Alignment Considerations
Proper alignment of sections is critical. Modern CPUs expect code and data to be aligned to specific boundaries for optimal performance and correctness. The assembler must ensure:
.text: 16-byte alignment.data: 8 or 16 bytes depending on structure usage.bss: 8 bytes or higher
This is enforced through padding in the file and section header sh_addralign fields.
9. Summary
The .text, .data, and .bss sections form the core of the ELF file’s content for code and static data. Properly generating their section headers ensures that the object file is compliant, linkable, and correctly interpretable by the linker and loader. An assembler must not only write section contents but also build complete and accurate metadata reflecting size, location, and usage semantics, all of which are encoded in the section headers.