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.
-
With the
LD_PRELOAD
environment variable. -
With the link-editors
-z interpose
option. -
With the
INTERPOSE
mapfile
keyword. -
As a consequence of a
singleton
symbol definition.
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.
An executable has additional flexibility, in that the executable can define individual interposing symbols using the INTERPOSE
mapfile
keyword. Because an 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] [ DEPEND INTERPOSE ] <self> exit
"INTERPOSE
" 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 ELF Symbol Visibility. 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.