Linker and Libraries Guide

Position-Independent Code

The code within a dynamic executable is typically position-dependent, and is tied to a fixed address in memory. Shared objects, on the other hand, can be loaded at different addresses in different processes. Position-independent code is not tied to a specific address. This independence allows the code to execute efficiently at a different address in each process that uses the code. Position-independent code is recommended for the creation of shared objects.

The compiler can generate position-independent code under the -K pic option.

If a shared object is built from position-dependent code, the text segment can require modification at runtime. This modification allows relocatable references to be assigned to the location that the object has been loaded. The relocation of the text segment requires the segment to be remapped as writable. This modification requires a swap space reservation, and results in a private copy of the text segment for the process. The text segment is no longer sharable between multiple processes. Position-dependent code typically requires more runtime relocations than the corresponding position-independent code. Overall, the overhead of processing text relocations can cause serious performance degradation.

When a shared object is built from position-independent code, relocatable references are generated as indirections through data in the shared object's data segment. The code within the text segment requires no modification. All relocation updates are applied to corresponding entries within the data segment. See Global Offset Table (Processor-Specific) and Procedure Linkage Table (Processor-Specific) for more details on the specific indirection techniques.

The runtime linker attempts to handle text relocations should these relocations exist. However, some relocations can not be satisfied at runtime.

The x64 position-dependent code sequence typically generates code which can only be loaded into the lower 32–bits of memory. The upper 32–bits of any address must all be zeros. Since shared objects are typically loaded at the top of memory, the upper 32–bits of an address are required. Position-dependent code within an x64 shared object is therefore insufficient to cope with relocation requirements. Use of such code within a shared object can result in runtime relocation errors.

$ prog prog: fatal: relocation error: R_AMD64_32: file \ symbol (unknown): value 0xfffffd7fff0cd457 does not fit

Position-independent code can be loaded in any region in memory, and hence satisfies the requirements of shared objects for x64.

This situation differs from the default ABS64 mode that is used for 64–bit SPARCV9 code. This position-dependent code is typically compatible with the full 64–bit address range. Thus, position-dependent code sequences can exist within SPARCV9 shared objects. Use of either the ABS32 mode, or ABS44 mode for 64–bit SPARCV9 code, can still result in relocations that can not be resolved at runtime. However, each of these modes require the runtime linker to relocate the text segment.

Regardless of the runtime linkers capabilities, or differences in relocation requirements, shared objects should be built using position-independent code.

You can identify a shared object that requires relocations against its text segment. The following example uses elfdump(1) to determine whether a TEXTREL entry dynamic entry exists.

$ cc -o -G -R. foo.c
$ elfdump -d | grep TEXTREL
       [9]  TEXTREL       0

Note –

The value of the TEXTREL entry is irrelevant. The presence of this entry in a shared object indicates that text relocations exist.

To prevent the creation of a shared object that contains text relocations use the link-editor's -z text flag. This flag causes the link-editor to generate diagnostics indicating the source of any position-dependent code used as input. The following example shows how position-dependent code results in a failure to generate a shared object.

$ cc -o -z text -G -R. foo.c
Text relocation remains                       referenced
    against symbol                  offset      in file
foo                                 0x0         foo.o
bar                                 0x8         foo.o
ld: fatal: relocations remain against allocatable but \
non-writable sections

Two relocations are generated against the text segment because of the position-dependent code generated from the file foo.o. Where possible, these diagnostics indicate any symbolic references that are required to carry out the relocations. In this case, the relocations are against the symbols foo and bar.

Text relocations within a shared object can also occur when hand written assembler code is included and does not include the appropriate position-independent prototypes.

Note –

You might want to experiment with some simple source files to determine coding sequences that enable position-independence. Use the compilers ability to generate intermediate assembler output.

SPARC: -K pic and -K PIC Options

For SPARC binaries, a subtle difference between the -K pic option and an alternative -K PIC option affects references to global offset table entries. See Global Offset Table (Processor-Specific).

The global offset table is an array of pointers, the size of whose entries are constant for 32–bit (4–bytes) and 64–bit (8–bytes). The following code sequence makes reference to an entry under -K pic.

        ld    [%l7 + j], %o0    ! load &j into %o0

Where %l7 is the precomputed value of the symbol _GLOBAL_OFFSET_TABLE_ of the object making the reference.

This code sequence provides a 13–bit displacement constant for the global offset table entry. This displacement therefore provides for 2048 unique entries for 32–bit objects, and 1024 unique entries for 64–bit objects. If the creation of an object requires more than the available number of entries, the link-editor produces a fatal error.

$ cc -K pic -G -o a.o b.o ... z.o
ld: fatal: too many symbols require `small' PIC references:
        have 2050, maximum 2048 -- recompile some modules -K PIC.

To overcome this error condition, compile some of the input relocatable objects with the -K PIC option. This option provides a 32–bit constant for the global offset table entry.

        sethi %hi(j), %g1
        or    %g1, %lo(j), %g1    ! get 32–bit constant GOT offset
        ld    [%l7 + %g1], %o0    ! load &j into %o0

You can investigate the global offset table requirements of an object using elfdump(1) with the -G option. You can also examine the processing of these entries during a link-edit using the link-editors debugging tokens -D got,detail.

Ideally, frequently accessed data items benefit from using the -K pic model. You can reference a single entry using both models. However, determining which relocatable objects should be compiled with either option can be time consuming, and the performance improvement realized small. A recompilation of all relocatable objects with the -K PIC option is typically easier.