Runtime linker

Updated: April 19, 2023

The runtime linker is invoked when a program that was linked against a shared object is started or when a program requests that a shared object be dynamically loaded. This linker is contained within the C runtime library.

The runtime linker performs several tasks when loading a shared library (.so file):

  1. If the requested shared library isn't already loaded in memory, the runtime linker loads it:
    • If the shared library name is fully qualified (i.e., begins with a slash), it's loaded directly from the specified location. If it can't be found there, no further searches are performed.
    • If it's not a fully qualified pathname, the linker searches for it as follows:
      1. If the executable's dynamic section contains a DT_RPATH tag, then the path specified by DT_RPATH is searched.
      2. If the shared library isn't found, the runtime linker searches for it in the directories specified by LD_LIBRARY_PATH.
        Note: For security reasons, the runtime linker unsets LD_LIBRARY_PATH if the binary has the setuid bit set.
      3. If the shared library still isn't found, then the linker searches for the default library search path as specified by the LD_LIBRARY_PATH environment variable to procnto (i.e., the CS_LIBPATH configuration string). If none has been specified, then the default library path is set to the image filesystem's path.
  2. Once the requested shared library is found, it's loaded into memory. For ELF shared libraries, this is a very efficient operation: the runtime linker simply needs to use the mmap() call twice to map the two load segments into memory.
  3. The shared library is then added to the internal list of all libraries that the process has loaded. The runtime linker maintains this list.
  4. The runtime linker then decodes the dynamic section of the shared object.

This dynamic section provides information to the linker about other libraries that this library was linked against. It also gives information about the relocations that need to be applied and the external symbols that need to be resolved. The runtime linker will first load any other required shared libraries (which may themselves reference other shared libraries). It will then process the relocations for each library. Some of these relocations are local to the library, while others require the runtime linker to resolve a global symbol. In the latter case, the linker will search through the list of libraries for this symbol. In ELF files, hash tables are used for the symbol lookup, so they're very fast. The order in which libraries are searched for symbols is very important, as we'll see in the section on Symbol name resolution below.

Once all relocations have been applied, any initialization functions that have been registered in the shared library's init section are called. This is used in some implementations of C++ to call global constructors.