symbol interposition
Suppose a program and several shared libraries all eventually call malloc(). Now you want to slip in your own version — perhaps to count allocations or detect leaks — without recompiling anything. Symbol interposition is the mechanism that lets one definition of a symbol stand in for another at load time, so that everyone's calls to malloc() route to your version instead of the C library's. It is how tools like leak detectors and memory profilers hook into a program from the outside.
In ELF dynamic linking, when a name is looked up, the dynamic linker searches the loaded objects in a defined order and the FIRST matching definition wins for everyone — that first definition interposes (preempts) all the later ones with the same name. You can force your library to the front of that search using the environment variable LD_PRELOAD, which loads your shared object before the rest, so your strong, default-visibility symbol named malloc is found first and used everywhere. This works only for symbols with default visibility; a HIDDEN symbol is not in the dynamic table and cannot be interposed, and a PROTECTED symbol cannot be interposed for calls made from inside its own object.
It matters as both a powerful tool and a hidden cost. The tool side: LD_PRELOAD interposition powers AddressSanitizer-style wrappers, fake clocks for testing, and drop-in allocator replacements like tcmalloc or jemalloc. The cost side: because ANY default-visibility symbol could in principle be interposed, the compiler often cannot assume a call to your own function stays within your library, so it must route the call through the GOT/PLT rather than calling it directly, which costs a little speed — one reason marking internal symbols hidden or building a position-independent executable can make code faster. A common surprise is that interposition is per symbol name and order-sensitive, so two preloaded libraries defining the same name interact in subtle, order-dependent ways.
// my_malloc.c -> build as libcount.so, then: void *malloc(size_t n) { count++; return real_malloc(n); // looked up via dlsym(RTLD_NEXT,...) } $ LD_PRELOAD=./libcount.so ./a.out # every malloc() now counted
Preloading a shared object puts its symbols first in the search order, so its malloc() interposes the C library's for the whole program.
Interposition only reaches default-visibility dynamic symbols; HIDDEN symbols cannot be interposed at all. The possibility of interposition is also why default-visibility calls may go through the PLT and be slightly slower than a direct call.