When an object requires a symbol, the runtime linker searches for that symbol based upon the requesting objects' symbol search scope, and the symbol visibility offered by each object within the process. These attributes are applied as defaults to an object at the time it is loaded, as specific modes to dlopen(3DL), and in some cases can be recorded within the object at the time it is built.
Typically, an average user becomes familiar with the default symbol search models that are applied to a dynamic executable and its dependencies, and to objects obtained through dlopen(3DL). The former is outlined in the next section "Default Lookup", and the latter, which is also able to exploit the various symbol lookup attributes, is discussed in "Symbol Lookup".
An alternative model for symbol lookup is provided when a dynamic object is created with the link-editors -Bdirect option (see "External Bindings"). This model directs the runtime linker to search for a symbol directly in the object that provided the symbol at link-edit time. This model is discussed in more detail in "Direct Binding".
A dynamic executable and all the dependencies loaded with it are assigned world search scope, and global symbol visibility (see "Symbol Lookup"). Thus, when the runtime linker looks up a symbol for a dynamic executable or for any of the dependencies loaded with the executable, it does so by searching each object, starting with the dynamic executable, and progressing through each dependency in the same order in which the objects were loaded.
As discussed in previous sections, ldd(1) lists the dependencies of a dynamic executable in the order in which they are loaded. Therefore, if the shared object libbar.so.1 requires the address of symbol foo to complete its relocation, and this shared object is a dependency of the dynamic executable prog:
$ ldd prog libfoo.so.1 => /home/me/lib/libfoo.so.1 libbar.so.1 => /home/me/lib/libbar.so.1
Then, the runtime linker first looks for foo in the dynamic executable prog, then in the shared object /home/me/lib/libfoo.so.1, and finally in the shared object /home/me/lib/libbar.so.1.
Symbol lookup can be an expensive operation, especially when the size of symbol names increases and the number of dependencies increases. This aspect of performance is discussed in more detail in "Performance Considerations". Also see "Direct Binding" for an alternative lookup model.
The runtime linker's default mechanism of searching for a symbol first in the dynamic executable and then in each of the dependencies means that the first occurrence of the required symbol will satisfy the search. Therefore, if more than one instance of the same symbol exists, the first instance will interpose on all others (see also "Shared Object Processing").
When creating an object using the link-editor's -Bdirect option, the relationship between the referenced symbol and the dependency that provided the definition is recorded in the object. The runtime linker uses this information to search directly for the symbol in the associated object, rather than carry out the default symbol search model.
The use of -Bdirect also enables lazy loading, which is equivalent to adding the option -zlazyload to the front of the link-edit command line (see "Lazy Loading of Dynamic Dependencies").
This direct binding model can significantly reduce the symbol lookup overhead within a dynamic process that has many symbolic relocations and many dependencies. This model also allows multiple symbols of the same name to be located from different objects that have been directly bound to.
However, direct bindings can circumvent the traditional use of interposition symbols because they bypasses the default search model. The default model ensures that all references to a symbol bind to one definition.
Interposition can still be achieved in a direct binding environment, on a per-object basis, if an object is identified as an interposer. Any object loaded using the environment variable
LD_PRELOAD (see "Loading Additional Objects"), or created with the link-editor's -zinterpose option, is identified as an interposer. When the runtime linker searches for a directly bound symbol, it first looks
in any object identified as an interposer before it looks in the object that supplies the symbol definition.