Sun Microsystems Logo
Products & Services
 
Support & Training
 
 

Previous Previous     Contents     Index     Next Next

Position-Independent Code

The compiler can generate position-independent code under the -K pic option. Whereas the code within a dynamic executable is usually tied to a fixed address in memory, position-independent code can be loaded anywhere in the address space of a process. Because the code is not tied to a specific address, it will execute correctly without page modification at a different address in each process that uses it. This code creates programs that require the smallest amount of page modification at runtime.

When you use position-independent code, relocatable references are generated as an indirection that use data in the shared object's data segment. The text segment code remains read-only, and 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 use of these two sections.

If a shared object is built from code that is not position-independent, the text segment will usually require a large number of relocations to be performed at runtime. Although the runtime linker is equipped to handle this, the system overhead this creates can cause serious performance degradation.

You can identify a shared object that requires relocations against its text segment. Use dump(1) and inspect the output for any TEXTREL entry. For example:

$ cc -o libfoo.so.1 -G -R. foo.c
$ dump -Lv libfoo.so.1 | grep TEXTREL
[9]     TEXTREL  0


Note - The value of the TEXTREL entry is irrelevant. Its presence 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 non-position-independent code used as input. Such code results in a failure to generate the intended shared object. For example:

$ cc -o libfoo.so.1 -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 non-position-independent 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.

Another common cause of creating text relocations when generating a shared object is by including hand-written assembler code that has not been coded with the appropriate position-independent prototypes.


Note - You may 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 an object is built that requires more than the available number of entries, the link-editor produces a fatal error:

$ cc -K pic -G -o lobfoo.so.1 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.

Remove Unused Material

The inclusion of functions and data that are not used by the object being built, is wasteful. This material bloats the object, which can result in unnecessary relocation overhead and associated paging activity. References to unused dependencies are also wasteful. These references result in the unnecessary loading and processing of other shared objects.

Unused sections are displayed during a link-edit when using the link-editors debugging token -D unused. Sections identified as unused should be removed from the link-edit, or eliminated using the link-editors -z ignore option.

The link-editor identifies a section from a relocatable object as unused if:

  • The section is allocatable

  • No other sections bind to (relocate) to this section

  • The section provides no global symbols

You can improve the link-editors ability to eliminate sections by defining the shared objects external interfaces. By defining an interface, global symbols that are not defined as part of the interface are reduced to locals. These reduced symbols, if unreferenced from other objects, are now clearly identified as candidates for elimination.

Individual functions and data variables can be eliminated by the link-editor if these items are assigned to their own sections. This section refinement is achieved using compiler options such as -xF. Earlier compilers only provided for the assignment of functions to their own sections. Newer compilers have extended the -xF syntax to assign data variables to their own sections. Earlier compilers required C++ exception handling to be disabled when using -xF. This restriction has been dropped with later compilers.

If all allocatable sections from a relocatable object can be eliminated, the entire file is discarded from the link-edit.

In addition to input file elimination, the link-editor also identifies unused dependencies. A dependency is deemed unused if it is not bound to by the object being produced. An object built with the -z ignore option, will have no unused dependencies recorded in it.

The -z ignore option applies only to the files that follow the option on the link-edit command line. The -z ignore option is cancelled with -z record.

Maximizing Shareability

As mentioned in Underlying System, only a shared object's text segment is shared by all processes that use it. The object's data segment typically is not shared. Each process using a shared object, generates a private memory copy of its entire data segment as data items within the segment are written to. Reduce the data segment, either by moving data elements that are never written to the text segment, or by removing the data items completely.

The following sections describe several mechanisms that can be used to reduce the size of the data segment.

Move Read-Only Data to Text

Data elements that are read-only should be moved into the text segment using const declarations. For example, the following character string resides in the .data section, which is part of the writable data segment:

char * rdstr = "this is a read-only string";

In contrast, the following character string resides in the .rodata section, which is the read-only data section contained within the text segment:

const char * rdstr = "this is a read-only string";

Reducing the data segment by moving read-only elements into the text segment is admirable. However, moving data elements that require relocations can be counterproductive. For example, examine the following array of strings:

char * rdstrs[] = { "this is a read-only string",
                    "this is another read-only string" };

A better definition might seem to be:

const char * const rdstrs[] = { ..... };

This definition ensures that the strings and the array of pointers to these strings are placed in a .rodata section. Unfortunately, although the user perceives the array of addresses as read-only, these addresses must be relocated at runtime. This definition therefore results in the creation of text relocations. Representing it as:

const char * rdstrs[] = { ..... };

insures the array pointers are maintained in the writable data segment where they can be relocated. The array strings are maintained in the read-only text segment.


Note - Some compilers, when generating position-independent code, can detect read-only assignments that result in runtime relocations. These compilers arrange for placing such items in writable segments. For example, .picdata.


Collapse Multiply-Defined Data

Data can be reduced by collapsing multiply-defined data. A program with multiple occurrences of the same error messages can be better off by defining one global datum, and have all other instances reference this. For example:

const char * Errmsg = "prog: error encountered: %d";

foo()
{
        ......
        (void) fprintf(stderr, Errmsg, error);
        ......

The main candidates for this sort of data reduction are strings. String usage in a shared object can be investigated using strings(1). The following example generates a sorted list of the data strings within the file libfoo.so.1. Each entry in the list is prefixed with the number of occurrences of the string.

$ strings -10 libfoo.so.1 | sort | uniq -c | sort -rn 

Previous Previous     Contents     Index     Next Next