|zSeries ELF Application Binary Interface Supplement|
|<<< Previous||Program loading and dynamic linking||Next >>>|
Dynamic section entries give information to the dynamic linker. Some of this information is processor-specific, including the interpretation of some entries in the dynamic structure.
The d_ptr field of this entry gives the address of the first byte in the Procedure Linkage Table (.PLT in the Section called Procedure Linkage Table).
This entry is associated with a table of relocation entries for the PLT. For zSeries this entry is mandatory both for executable and shared object files. Moreover, the relocation table's entries must have a one-to-one correspondence with the PLT. The table of DT_JMPREL relocation entries is wholly contained within the DT_RELA referenced table. See the Section called Procedure Linkage Table for more information.
Position-independent code cannot, in general, contain absolute virtual addresses. Global Offset Tables hold absolute addresses in private data, thus making the addresses available without compromising the position-independence and sharability of a program's text. A program references its GOT using position-independent addressing and extracts absolute values, thus redirecting position-independent references to absolute locations.
When the dynamic linker creates memory segments for a loadable object file, it processes the relocation entries, some of which will be of type R_390_GLOB_DAT, referring to the GOT. The dynamic linker determines the associated symbol values, calculates their absolute addresses, and sets the GOT entries to the proper values. Although the absolute addresses are unknown when the linkage editor builds an object file, the dynamic linker knows the addresses of all memory segments and can thus calculate the absolute addresses of the symbols contained therein.
A GOT entry provides direct access to the absolute address of a symbol without compromising position-independence and sharability. Because the executable file and shared objects have separate GOTs, a symbol may appear in several tables. The dynamic linker processes all the GOT relocations before giving control to any code in the process image, thus ensuring the absolute addresses are available during execution.
The dynamic linker may choose different memory segment addresses for the same shared object in different programs; it may even choose different library addresses for different executions of the same program. Nevertheless, memory segments do not change addresses once the process image is established. As long as a process exists, its memory segments reside at fixed virtual addresses.
The format and interpretation of the Global Offset Table is processor specific. For zSeries the symbol _GLOBAL_OFFSET_TABLE_ may be used to access the table. The symbol refers to the start of the .got section. Two words in the GOT are reserved:
The word at _GLOBAL_OFFSET_TABLE_ is set by the linkage editor to hold the address of the dynamic structure, referenced with the symbol _DYNAMIC. This allows a program, such as the dynamic linker, to find its own dynamic structure without having yet processed its relocation entries. This is especially important for the dynamic linker, because it must initialize itself without relying on other programs to relocate its memory image.
The word at _GLOBAL_OFFSET_TABLE_ is reserved for future use.
The Global Offset Table resides in the ELF .got section.
References to a function address from an executable file and from the shared objects associated with the file must resolve to the same value. References from within shared objects will normally be resolved (by the dynamic linker) to the virtual address of the function itself. References from within the executable file to a function defined in a shared object will normally be resolved (by the linkage editor) to the address of the Procedure Linkage Table entry for that function within the executable file.
To allow comparisons of function addresses to work as expected, if an executable file references a function defined in a shared object, the linkage editor will place the address of the PLT entry for that function in its associated symbol table entry. See the Section called Symbol Values in the Chapter called Object files for details. The dynamic linker treats such symbol table entries specially. If the dynamic linker is searching for a symbol and encounters a symbol table entry for that symbol in the executable file, it normally follows these rules:
If the st_shndx field of the symbol table entry is not SHN_UNDEF, the dynamic linker has found a definition for the symbol and uses its st_value field as the symbol's address.
If the st_shndx field is SHN_UNDEF and the symbol is of type STT_FUNC and the st_value field is not zero, the dynamic linker recognizes this entry as special and uses the st_value field as the symbol's address.
Otherwise, the dynamic linker considers the symbol to be undefined within the executable file and continues processing.
Some relocations are associated with PLT entries. These entries are used for direct function calls rather than for references to function addresses. These relocations are not treated specially as described above because the dynamic linker must not redirect PLT entries to point to themselves.
Much as the Global Offset Table redirects position-independent address calculations to absolute locations, the Procedure Linkage Table redirects position-independent function calls to absolute locations. The linkage editor cannot resolve execution transfers (such as function calls) from one executable or shared object to another, so instead it arranges for the program to transfer control to entries in the PLT. The dynamic linker determines the absolute addresses of the destinations and stores them in the GOT, from which they are loaded by the PLT entry. The dynamic linker can thus redirect the entries without compromising the position-independence and sharability of the program text. Executable files and shared object files have separate PLTs.
As mentioned above, a relocation table is associated with the PLT. The DT_JMPREL entry in the _DYNAMIC array gives the location of the first relocation entry. The relocation table entries match the PLT entries in a one-to-one correspondence (relocation table entry 1 applies to PLT entry 1 and so on). The relocation type for each entry shall be R_390_JMP_SLOT. The relocation offset shall specify the address of the GOT entry containing the address of the function and the symbol table index shall reference the appropriate symbol.
To illustrate Procedure Linkage Tables, Figure 3 shows how the linkage editor might initialize the PLT when linking a shared executable or shared object.
* # PLT
for executables (not position independent)
PLT1 BASR 1,0 # Establish base
BASE1 L 1,AGOTENT-BASE1(1) # Load address of the GOT entry
L 1,0(0,1) # Load function address from the GOT
BCR 15,1 # Jump to address
RET1 BASR 1,0 # Return from GOT first time (lazy
BASE2 L 1,ASYMOFF-BASE2(1) # Load offset in symbol table to r1
BRC 15,-x # Jump to start of PLT
.word 0 # Filler
AGOTENT .long ? # Address of the GOT entry
ASYMOFF .long ? # Offset into the symbol table
* # PLT for shared objects (position
PLT1 LARL 1,<fn>@GOTENT # Load address of GOT entry in
LG 1,0(1) # Load function address from the GOT
BCR 15,1 # Jump to address
RET1 BASR 1,0 # Return from GOT first time (lazy
BASE2 LGF 1,ASYMOFF-BASE2(1) # Load offset in symbol table to r1
BRCL 15,-x # Jump to start of PLT
ASYMOFF .long ? # Offset into symbol
Figure 3. Procedure Linkage Table Example
As described below the dynamic linker and the program cooperate to resolve symbolic references through the PLT. Again, the details described below are for explanation only. The precise execution-time behavior of the dynamic linker is not specified.
The caller of a function in a different shared object transfers control to the start of the PLT entry associated with the function.
The first part of the PLT entry loads the address from the GOT entry associated with the function to be called. The control is transferred to the code referenced by the address. If the function has already been called at least once, or lazy binding is not used, then the address found in the GOT is the address of the function.
If a function has never been called and lazy binding is used then the address in the GOT points to the second half of the PLT. The second half loads the offset in the symbol table associated with the called function. Control is then transferred to the special first entry of the PLT.
This first entry of the PLT entry (Figure 4) calls the dynamic linker giving it the offset into the symbol table and the address of a structure that identifies the location of the caller.
The dynamic linker finds the real address of the symbol. It will store this address in the GOT entry of the function in the object code of the caller and it will then transfer control to the function.
Subsequent calls to the function from this object will find the resolved address in the first half of the PLT entry and will transfer control directly without invoking the dynamic linker.
* # PLT0
for static object (not position-independent)
PLT0 ST 1,28(15) # R1 has offset into symbol table
BASR 1,0 # Establish base
BASE1 L 1,AGOT-BASE1(1) # Get address of GOT
MVC 24(4,15),4(1) # Move loader info to stack
L 1,8(1) # Get address of loader
BR 1 # Jump to loader
.word 0 # Filler
AGOT .long got # Address of GOT
# PLT0 for shared object
PLT0 STG 1,56(15) # R1 has offset into symbol table
MVC 48(8,15),8(1) # move loader info (object struct
address) to stack
LG 1,16(12) # Entry address of loader in R1
BCR 15,1 # Jump to
Figure 4. Special first entry in Procedure Linkage Table
The LD_BIND_NOW environment variable can change dynamic linking behavior. If its value is not null the dynamic linker resolves the function call binding at load time, before transferring control to the program. In other words the dynamic linker processes relocation entries of type R_390_JMP_SLOT during process initialization. If LD_BIND_NOW is null the dynamic linker evaluates PLT entries lazily, delaying symbol resolution and relocation until the first execution of a table entry.
Lazy binding generally improves overall application performance because unused symbols do not incur the overhead of dynamic linking. Nevertheless, two situations make lazy binding undesirable for some applications: