Introduction to Position Independent Code
1.
Introduction to PIC - (Position Independent Code)
PIC code radically differs from conventional code in the way it
calls functions and operates on data variables.
It will access these functions and data through an indirection table,
the "Global Offset Table" (GOT), by software convention accessible using
the reserved name "_GLOBAL_OFFSET_TABLE_".
The exact mechanism used for this is hardware architecture dependent,
but usually a special machine register is reserved for setting up
the location of the GOT when entering a function.
The rationale behind this indirect addressing is to generate code
that can be independently accessed of the actual load address.
In a true PIC library without relocations in the text segment,
only the symbols exported in the "Global Offset Table" need updating
at run-time depending on the current load address of the various
shared libraries in the address space of the running process.
Likewise, procedure calls to globally defined functions are redirected
through the "Procedure Linkage Table" (PLT) residing in the data segment
of the core image. Again, this is done to avoid run-time modifications
to the text segment.
The linker-editor allocates the Global Offset Table and Procedure
Linkage Table when combining PIC object files into an image suitable for
mapping into the process address space. It also collects all symbols
that may be needed by the run-time link-editor and stores these along
with the image's text and data bits. Another reserved symbol, _DYNAMIC
is used to indicate the presence of the run-time linker structures.
Whenever _DYNAMIC is relocated to 0, there is no need to invoke the
run-time link- editor. If this symbol is non-zero, it points at a data
structure from which the location of the necessary relocation- and
symbol information can be derived. This is most notably used by the
start-up module, crt0, crt1S and more recently Scrt1. The _DYNAMIC
structure is conventionally located at the start of the data segment of
the image to which it pertains.
On most architectures, when you compile source code to object code, you
need to specify whether the object code should be position independent
or not. There are occasional architectures which don't make the
distinction, usually because all object code is position independent by
virtue of the Application Binary Interface (ABI), or less often because
the load address of the object is fixed at compile time, which implies
that shared libraries are not supported by such a platform.
If an object is compiled as position independent code (PIC),
then the operating system can load the object at any address
in preparation for execution. This involves a time overhead,
in replacing direct address references with relative addresses at
compile time, and a space overhead, in maintaining information to help
the runtime loader fill in the unresolved addresses at runtime.
Consequently, PIC objects are usually slightly larger and slower at
runtime than the equivalent non-PIC object. The advantage of sharing
library code on disk and in memory outweigh these problems as soon as
the PIC object code in shared libraries is reused.
PIC compilation is exactly what is required for objects which will
become part of a shared library. Consequently, libtool builds PIC
objects for use in shared libraries and non-PIC objects for use in
static libraries. Whenever libtool instructs the compiler to generate a
PIC object, it also defines the preprocessor symbol, `PIC', so that
assembly code can be aware of whether it will reside in a PIC object or
not.
Typically, as libtool is compiling sources, it will generate a `.lo'
object, as PIC, and a `.o' object, as non-PIC, and then it will use the
appropriate one of the pair when linking executables and libraries of
various sorts. On architectures where there is no distinction, the `.lo'
file is just a soft link to the `.o' file.
In practice, you can link PIC objects into a static archive for a small
overhead in execution and load speed, and often you can similarly link
non-PIC objects into shared archives.
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.
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 using tools such as 'readelf -d foo' and inspect the output
for any TEXTREL entry. The value of the TEXTREL entry is irrelevant. Its
presence in a shared object indicates that text relocations exist.
References:
The contents of this document, unless otherwise expressly stated, are licensed under the CC-BY-SA-2.5 license. The Gentoo Name and Logo Usage Guidelines apply.
|