Runtime Linker
As part of the initialization and execution of a dynamic executable, an interpreter is called to complete the binding of the application to its dependencies. In the Solaris operating environment this interpreter is referred to as the runtime linker.
During the link-editing of a dynamic executable, a special .interp section, together with an associated program header, are created. This section contains a path name specifying the program's interpreter. The default name supplied by the link-editor is that of the runtime linker: /usr/lib/ld.so.1 for a 32-bit executable and /usr/lib/64/ld.so.1 for a 64-bit executable.
Note - ld.so.1 is a special case of a shared object and therefore can be versioned. Here a version number of 1 is used; however, later Solaris releases might provide higher version numbers.
During the process of executing a dynamic object the kernel loads the file and, using the program header information (see "Program Header"), locates the name of the required interpreter. The kernel loads this interpreter and transfers control to it, passing sufficient information to enable the interpreter to continue binding the application and run it.
In addition to initializing an application, the runtime linker provides services that enable the application to extend its address space by loading additional objects and binding to symbols within them.
The runtime linker:
Analyzes the executable's dynamic information section (.dynamic) and determines what dependencies are required.
Locates and loads in these dependencies, and analyzes their dynamic information sections to determine if any additional dependencies are required.
Performs any necessary relocations to bind these objects in preparation for process execution.
Calls any initialization functions provided by the dependencies.
Passes control to the application.
Can be called upon during the application's execution ,to perform any delayed function binding.
Can be called upon by the application to acquire additional objects with dlopen(3DL), and bind to symbols within these objects with dlsym(3DL).
Shared Object Dependencies
When the runtime linker creates the memory segments for a program, the dependencies tell what shared objects are needed to supply the program's services. By repeatedly connecting referenced shared objects and their dependencies, the runtime linker generates a complete process image.
Note - Even when a shared object is referenced multiple times in the dependency list, the runtime linker will connect the object only once to the process.
Locating Shared Object Dependencies
During the link-edit of a dynamic executable, one or more shared objects are explicitly referenced. These objects are recorded as dependencies within the dynamic executable.
The runtime linker first locates this dependency information and uses it to locate and load the associated objects. These dependencies are processed in the same order as they were referenced during the link-edit of the executable.
Once all the dynamic executable's dependencies are loaded, they too are inspected, in the order they are loaded, to locate any additional dependencies. This process continues until all dependencies are located and loaded. This technique results in a breadth-first ordering of all dependencies.
Directories Searched by the Runtime Linker
By default, the runtime linker looks in only one standard place for dependencies: /usr/lib for 32-bit dependencies, or /usr/lib/64 for 64-bit dependencies. Any dependency specified as a simple file name is prefixed with this default directory name and the resulting path name is used to locate the actual file.
The actual dependencies of any dynamic executable or shared object can be displayed using ldd(1). For example, the file /usr/bin/cat has the following dependencies:
$ ldd /usr/bin/cat libc.so.1 => /usr/lib/libc.so.1 libdl.so.1 => /usr/lib/libdl.so.1 |
The file /usr/bin/cat has a dependency, or needs, the files libc.so.1 and libdl.so.1.
The dependencies recorded in a file can be inspected by using the dump(1) command to display the file's .dynamic section, and referencing any entries that have a NEEDED tag. In the following example, the dependency libdl.so.1, displayed in the previous ldd(1) example, is not recorded in the file /usr/bin/cat. ldd(1) shows the total dependencies of the specified file, and libdl.so.1 is actually a dependency of /usr/lib/libc.so.1.
$ dump -Lvp /usr/bin/cat /usr/bin/cat: [INDEX] Tag Value [1] NEEDED libc.so.1 ......... |
In the previous dump(1) example, the dependencies are expressed as simple file names. In other words, there is no `/' in the name. The use of a simple file name requires the runtime linker to generate the required path name from a set of rules. File names that contain an embedded `/' will be used as provided.
The simple file name recording is the standard, most flexible mechanism of recording dependencies. The -h option of the link-editor records a simple name within the dependency. See "Naming Conventions" and "Recording a Shared Object Name".
Frequently, dependencies are distributed in directories other than /usr/lib or /usr/lib/64. If a dynamic executable or shared object needs to locate dependencies in another directory, the runtime linker must explicitly be told to search this directory.
The recommended way to indicate additional search paths to the runtime linker is to record a runpath during the link-edit of the dynamic executable or shared object. See "Directories Searched by the Runtime Linker" for details on recording this information.
Any runpath recording can be displayed using dump(1) and referring to the entry that has the RUNPATH tag. In the following example, prog has a dependency on libfoo.so.1. The runtime linker must search directories /home/me/lib and /home/you/lib before it looks in the default location /usr/lib.
$ dump -Lvp prog prog: [INDEX] Tag Value [1] NEEDED libfoo.so.1 [2] NEEDED libc.so.1 [3] RUNPATH /home/me/lib:/home/you/lib ......... |
Another way to add to the runtime linker's search path is to set the environment variable LD_LIBRARY_PATH. This environment variable, which is analyzed once at process startup, can be set to a colon-separated list of directories. These directories will be searched by the runtime linker before any runpath specification or default directory.
These environment variables are well suited to debugging purposes, such as forcing an application to bind to a local dependency. In the following example, the file prog from the previous example is bound to libfoo.so.1, found in the present working directory.
$ LD_LIBRARY_PATH=. prog |
Although useful as a temporary mechanism of influencing the runtime linker's search path, the use of the LD_LIBRARY_PATH environment variable is strongly discouraged in production software. Any dynamic executables that can reference this environment variable will have their search paths augmented. This augmentation can result in an overall degradation in performance. Also, as pointed out in "Using an Environment Variable" and "Directories Searched by the Runtime Linker", the LD_LIBRARY_PATH environment variable affects the link-editor.
A process can inherit an environment such that a 64-bit executable is given a search path that contains a 32-bit library matching the name being looked for, or vice versa. The runtime linker then rejects the mismatched 32-bit library and continues down its search path looking for a valid 64-bit match. If no match is found, an error message is generated. This can be observed in detail by setting the LD_DEBUG environment variable to include the files token. See "Debugging Library".
$ LD_LIBRARY_PATH=/usr/bin/64 LD_DEBUG=files /usr/bin/ls ... 00283: file=libc.so.1; needed by /usr/bin/ls 00283: 00283: file=/usr/lib/64/libc.so.1 rejected: ELF class mismatch: \ 00283: 32-bit/64-bit 00283: 00283: file=/usr/lib/libc.so.1 [ ELF ]; generating link map 00283: dynamic: 0xef631180 base: 0xef580000 size: 0xb8000 00283: entry: 0xef5a1240 phdr: 0xef580034 phnum: 3 00283: lmid: 0x0 00283: 00283: file=/usr/lib/libc.so.1; analyzing [ RTLD_GLOBAL RTLD_LAZY ] ... |
If a dependency cannot be located, ldd(1) indicates that the object cannot be found. Any attempt to execute the application results in an appropriate error message from the runtime linker:
$ ldd prog libfoo.so.1 => (file not found) libc.so.1 => /usr/lib/libc.so.1 libdl.so.1 => /usr/lib/libdl.so.1 $ prog ld.so.1: prog: fatal: libfoo.so.1: open failed: No such file or directory |