Segment Contents
An object file segment consists of one or more sections, though this fact is transparent to the program header. Whether the file segment holds one or many sections also is immaterial to program loading. Nonetheless, various data must be present for program execution, dynamic linking, and so on. The diagrams below illustrate segment contents in general terms. The order and membership of sections within a segment can vary.
Text segments contain read-only instructions and data. Data segments contain writable data and instructions. See Table 7-17 for a list of all special sections.
A PT_DYNAMIC program header element points at the .dynamic section. The .got and .plt sections also hold information related to position-independent code and dynamic linking.
The .plt can reside in a text or a data segment, depending on the processor. See "Global Offset Table (Processor-Specific)" and "Procedure Linkage Table (Processor-Specific)" for details.
The .bss section has the type SHT_NOBITS. Although it occupies no space in the file, it contributes to the segment's memory image. Normally, these uninitialized data reside at the end of the segment, thereby making p_memsz larger than p_filesz in the associated program header element.
Program Loading (Processor-Specific)
As the system creates or augments a process image, it logically copies a file's segment to a virtual memory segment. When, and if, the system physically reads the file depends on the program's execution behavior, system load, and so forth.
A process does not require a physical page unless it references the logical page during execution, and processes commonly leave many pages unreferenced. Therefore, delaying physical reads frequently obviates them, improving system performance. To obtain this efficiency in practice, executable and shared object files must have segment images whose file offsets and virtual addresses are congruent, modulo the page size.
Virtual addresses and file offsets for 32-bit segments are congruent modulo 64K (0x10000). Virtual addresses and file offsets for 64-bit segments are congruent modulo 1 megabyte (0x100000). By aligning segments to the maximum page size, the files are suitable for paging regardless of physical page size.
By default, 64-bit SPARC programs are linked with a starting address of 0x100000000. The whole program is above 4 gigabytes, including its text, data, heap, stack, and shared object dependencies. This helps ensure that 64-bit programs are correct because the program will fault in the least significant 4 gigabytes of its address space if it truncates any of its pointers. While 64-bit programs are linked above 4 gigabytes, you can still link them below 4 gigabytes by using a mapfile and the -M option to the compiler or link-editor. See /usr/lib/ld/sparcv9/map.below4G.
The following figure presents the SPARC version of the executable file.
Figure 7-7 SPARC: Executable File (64K alignment)
The following table defines the loadable segment elements for the previous figure.
Table 7-38 SPARC: ELF Program Header Segments (64K alignment)
Member | Text | Data |
---|---|---|
p_type | PT_LOAD | PT_LOAD |
p_offset | 0x0 | 0x4000 |
p_vaddr | 0x10000 | 0x24000 |
p_paddr | Unspecified | Unspecified |
p_filesize | 0x3a82 | 0x4f5 |
p_memsz | 0x3a82 | 0x10a4 |
p_flags | PF_R + PF_X | PF_R + PF_W + PF_X |
p_align | 0x10000 | 0x10000 |
The following figure presents the IA version of the executable file.
Figure 7-8 IA: Executable File (64K alignment)
The following table defines the loadable segment elements for the previous figure.
Table 7-39 IA: ELF Program Header Segments (64K alignment)
Member | Text | Data |
---|---|---|
p_type | PT_LOAD | PT_LOAD |
p_offset | 0x0 | 0x4000 |
p_vaddr | 0x8050000 | 0x8064000 |
p_paddr | Unspecified | Unspecified |
p_filesize | 0x32fd | 0x3a0 |
p_memsz | 0x32fd | 0xdc4 |
p_flags | PF_R + PF_X | PF_R + PF_W + PF_X |
p_align | 0x10000 | 0x10000 |
The example's file offsets and virtual addresses are congruent modulo the maximum page size for both text and data. Up to four file pages hold impure text or data depending on page size and file system block size.
The first text page contains the ELF header, the program header table, and other information.
The last text page holds a copy of the beginning of data.
The first data page has a copy of the end of text.
The last data page can contain file information not relevant to the running process. Logically, the system enforces the memory permissions as if each segment were complete and separate The segments addresses are adjusted to ensure that each logical page in the address space has a single set of permissions. In the examples above, the region of the file holding the end of text and the beginning of data will be mapped twice: at one virtual address for text and at a different virtual address for data.
Note - The examples above reflect typical Solaris system binaries that have their text segments rounded.
The end of the data segment requires special handling for uninitialized data, which the system defines to begin with zero values. If a file's last data page includes information not in the logical memory page, the extraneous data must be set to zero, not the unknown contents of the executable file.
Impurities in the other three pages are not logically part of the process image. Whether the system expunges these impurities is unspecified. The memory image for this program is shown in the following figures, assuming 4 Kbyte (0x1000) pages. For simplicity, these figures illustrate only one page size.
Figure 7-9 SPARC: Process Image Segments
Figure 7-10 IA: Process Image Segments
One aspect of segment loading differs between executable files and shared objects. Executable file segments typically contain absolute code. For the process to execute correctly, the segments must reside at the virtual addresses used to create the executable file. The system uses the p_vaddr values unchanged as virtual addresses.
On the other hand, shared object segments typically contain position-independent code. This code enables a segment's virtual address change from one process to another, without invalidating execution behavior.
Though the system chooses virtual addresses for individual processes, it maintains the relative positions of the segments. Because position-independent code uses relative addressing between segments, the difference between virtual addresses in memory must match the difference between virtual addresses in the file.
The following tables show possible shared object virtual address assignments for several processes, illustrating constant relative positioning. The tables also include the base address computations.
Table 7-40 SPARC: ELF Example Shared Object Segment Addresses
Source | Text | Data | Base Address |
---|---|---|---|
File | 0x0 | 0x4000 | 0x0 |
Process 1 | 0xc0000000 | 0xc0024000 | 0xc0000000 |
Process 2 | 0xc0010000 | 0xc0034000 | 0xc0010000 |
Process 3 | 0xd0020000 | 0xd0024000 | 0xd0020000 |
Process 4 | 0xd0030000 | 0xd0034000 | 0xd0030000 |
Table 7-41 IA: ELF Example Shared Object Segment Addresses
Source | Text | Data | Base Address |
---|---|---|---|
File | 0x0 | 0x4000 | 0x0 |
Process 1 | 0x8000000 | 0x8004000 | 0x80000000 |
Process 2 | 0x80081000 | 0x80085000 | 0x80081000 |
Process 3 | 0x900c0000 | 0x900c4000 | 0x900c0000 |
Process 4 | 0x900c6000 | 0x900ca000 | 0x900c6000 |
Program Interpreter
A dynamic executable or shared object that initiates dynamic linking can have one PT_INTERP program header element. During exec(2), the system retrieves a path name from the PT_INTERP segment and creates the initial process image from the interpreter file's segments. The interpreter is responsible for receiving control from the system and providing an environment for the application program.
In the Solaris operating environment the interpreter is known as the runtime linker, ld.so.1(1).
Runtime Linker
When creating a dynamic object that initiates dynamic linking, the link-editor adds a program header element of type PT_INTERP to an executable file. This element instructing the system to invoke the runtime linker as the program interpreter. exec(2) and the runtime linker cooperate to create the process image for the program.
The link-editor constructs various data for executable and shared object files that assist the runtime linker. These data reside in loadable segments, making them available during execution. These segments include:
A .dynamic section with type SHT_DYNAMIC that holds various data. The structure residing at the beginning of the section holds the addresses of other dynamic linking information.
The .got and .plt sections with type SHT_PROGBITS that hold two separate tables: the global offset table and the procedure linkage table. Sections below explain how the runtime linker uses and changes the tables to create memory images for object files.
The .hash section with type SHT_HASH that holds a symbol hash table.
Shared objects can occupy virtual memory addresses that are different from the addresses recorded in the file's program header table. The runtime linker relocates the memory image, updating absolute addresses before the application gains control.