By default, the runtime linker searches for a symbol first in the dynamic executable and then in each dependency. With this model, the first occurrence of the required symbol satisfies the search. Therefore, if more than one instance of the same symbol exists, the first instance interposes on all others.
An overview of how symbol resolution is affected by interposition is provided in Simple Resolutions. A mechanism for changing symbol visibility, and hence reducing the chance of accidental interposition is provided in Reducing Symbol Scope.
Interposition can be enforced, on a per-object basis, if an object is explicitly identified as an interposer. Any object loaded using the environment variable LD_PRELOAD or created with the link-editor's -z interpose option, is identified as an interposer. When the runtime linker searches for a symbol, any object identified as an interposer is searched after the application, but before any other dependencies.
The use of all of the interfaces offered by an interposer can only be guaranteed if the interposer is loaded before any process relocation has occurred. Interposers provided using the environment variable LD_PRELOAD, or established as non-lazy loaded dependencies of the application, are loaded before relocation processing starts. Interposers that are brought into a process after relocation has started are demoted to normal dependencies. Interposers can be demoted if the interposer is lazy loaded, or loaded as a consequence of using dlopen(3C). The former category can be detected using ldd(1).
$ ldd -Lr prog libc.so.1 => /lib/libc.so.1 foo.so.2 => ./foo.so.2 libmapmalloc.so.1 => /usr/lib/libmapmalloc.so.1 loading after relocation has started: interposition request \ (DF_1_INTERPOSE) ignored: /usr/lib/libmapmalloc.so.1
If the link-editor encounters an explicitly defined interposer while processing dependencies for lazy loading, the interposer is recorded as a non-lazy loadable dependency.