Dynamic Linking

Dynamic Section

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.

DT_PLTGOT

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).

DT_JMPREL

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.

Global Offset Table

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 Global Offset Table resides in the ELF .got section.

Function Addresses

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:

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.

Procedure Linkage Table

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

to r1

          BCR   15,1               # Jump to address

RET1      BASR  1,0                # Return from GOT first time (lazy

binding)

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

independent)

PLT1      LARL  1,<fn>@GOTENT      # Load address of GOT entry in

r1

          LG    1,0(1)             # Load function address from the GOT

to r1

          BCR   15,1               # Jump to address

RET1      BASR  1,0                # Return from GOT first time (lazy

binding)

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

table

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.

  1. The caller of a function in a different shared object transfers control to the start of the PLT entry associated with the function.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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

(position-independent)

PLT0      STG   1,56(15)        # R1 has offset into symbol table

          LARL  1,_GLOBAL_OFFSET_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

loader

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.

Note

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:

  1. The initial reference to a shared object function takes longer than subsequent calls because the dynamic linker intercepts the call to resolve the symbol, and some applications cannot tolerate this unpredictability.

  2. If an error occurs and the dynamic linker cannot resolve the symbol, the dynamic linker will terminate the program. Under lazy binding, this might occur at arbitrary times. Once again, some applications cannot tolerate this unpredictability. By turning off lazy binding, the dynamic linker forces the failure to occur during process initialization, before the application receives control.