Linker and Libraries Guide

Defining Explicit Interposition

The default search model can result in instances of the same named symbol interposing on later instances of the same name. Even without any explicit labelling, interposition still occurs, so that one symbol definition is bound to from all references. This implicit interposition occurs as a consequence of the symbol search, not because of any explicit instruction the runtime linker has been given. This implicit interposition can be circumvented by direct bindings.

Although direct bindings work to resolve a symbol reference directly to an associated symbol definition, explicit interposition is processed prior to any direct binding search. Therefore, even within a direct binding environment, interposers can be designed, and be expected to interpose on any direct binding associations. Interposers can be explicitly defined using the following techniques.

The interposition facilities of the LD_PRELOAD environment variable, and the -z interpose option, have been available for some time. See Runtime Interposition. As these objects are explicitly defined to be interposers, the runtime linker inspects these objects before processing any direct binding.

Interposition that is established for a shared object applies to all the interfaces of that dynamic object. This object interposition is established when a object is loaded using the LD_PRELOAD environment variable. Object interposition is also established when an object that has been built with the -z interpose option, is loaded. This object model is important when techniques such as dlsym(3C) with the special handle RTLD_NEXT are used. An interposing object should always have a consistent view of the next object.

A dynamic executable has additional flexibility, in that the executable can define individual interposing symbols using the INTERPOSE mapfile keyword. Because a dynamic executable is the first object loaded in a process, the executables view of the next object is always consistent.

The following example shows an application that explicitly wants to interpose on the exit() function.


$ cat mapfile
$mapfile_version 2
SYMBOL_SCOPE {
        global:
                exit    { FLAGS = INTERPOSE };
};
$ cc -o prog -M mapfile exit.c a.c b.c ...
$ elfdump -y prog | fgrep exit
    [6]  DI          <self>         exit

The letter “I” indicates the interposing nature of this symbol. Presumably, the implementation of this exit() function directly references the system function _exit(), or calls through to the system function exit() using dlsym() with the RTLD_NEXT handle.

At first, you might consider identifying this object using the -z interpose option. However, this technique is rather heavy weight, because all of the interfaces exported by the application would act as interposers. A better alternative would be to localize all of the symbols provided by the application except for the interposer, together with using the -z interpose option.

However, use of the INTERPOSE mapfile keyword provides greater flexibility. The use of this keyword allows an application to export several interfaces while selecting those interfaces that should act as interposers.

Symbols that are assigned the STV_SINGLETON visibility effectively provide a form of interposition. See Table 7–20. These symbols can be assigned by the compilation system to an implementation that might become multiply instantiated in a number of objects within a process. All references to a singleton symbol are bound to the first occurrence of a singleton symbol within a process.