my-server
← Wiki

Rpath

In computer science, rpath designates the run-time search path hard-coded in an executable file or library. Dynamic linking loaders use the rpath to find required libraries.

Specifically, it encodes a path to shared libraries into the header of an executable (or another shared library). This RPATH header value (so named in the Executable and Linkable Format header standards) may either override or supplement the system default dynamic linking search paths.

The rpath of an executable or shared library is an optional entry in the <code>.dynamic</code> section of the ELF executable or shared libraries, with the type <code>DT_RPATH</code>, called the <code>DT_RPATH</code> attribute. It can be stored there at link time by the linker. Tools such as <code>chrpath</code> and <code>patchelf</code> can create or modify the entry later.

Background

Modern computer executables use dynamic libraries, which are separate loadable files, to reduce the duplication of code. When such an executable is run, the dynamic linker <code>ld.so</code> goes through the dynamic library entries and loads them for use by the executable program. Because libraries can be located in many possible locations, the ELF format has the <code>DT_RPATH</code> entry to specify to <code>ld.so</code> where to find the libraries.

The different dynamic linkers for ELF implement the use of the <code>DT_RPATH</code> attribute in different ways. The main divergence lies in:

  • Handling of the new entry <code>DT_RUNPATH</code>, a more restricted version of rpath.
  • Whether relative paths are available via <code>$ORIGIN</code>.
  • Whether other variables such as <code>$PLATFORM</code>, <code>$LIB</code> are available.

GNU ld.so

The dynamic linker of the GNU C Library searches for shared libraries in the following locations in order:

  1. The (colon-separated) paths in the <code>DT_RPATH</code> dynamic section attribute of the binary if present and the <code>DT_RUNPATH</code> attribute does not exist.
  2. The (colon-separated) paths in the environment variable <code>LD_LIBRARY_PATH</code>, unless the executable is a <code>setuid/setgid</code> binary, in which case it is ignored. <code>LD_LIBRARY_PATH</code> can be overridden by calling the dynamic linker with the option <code>--library-path</code> (e.g. <code>/lib/ld-linux.so.2 --library-path $HOME/mylibs myprogram</code>).
  3. The (colon-separated) paths in the <code>DT_RUNPATH</code> dynamic section attribute of the binary if present.
  4. Lookup based on the <code>ldconfig</code> cache file (often located at <code>/etc/ld.so.cache</code>) which contains a compiled list of candidate libraries previously found in the augmented library path (set by <code>/etc/ld.so.conf</code>). If, however, the binary was linked with the <code>-z nodefaultlib</code> linker option, libraries in the default library paths are skipped.
  5. In the trusted default path <code>/lib</code>, and then <code>/usr/lib</code>. If the binary was linked with the <code>-z nodefaultlib</code> linker option, this step is skipped.

Failing to find the shared library in all these locations will raise the "cannot open shared object file: No such file or directory" error.

Notes:

  • <code>readelf -d <binary_name> | grep 'R.*PATH'</code> displays the RPATH or RUNPATH of a binary file. In gcc, for instance, one could specify RPATH by <code>-Wl,-rpath,/custom/rpath/</code>.
  • The option <code>--inhibit-rpath LIST</code> of the dynamic linker instructs it to ignore <code>DT_RPATH</code> and <code>DT_RUNPATH</code> attributes of the object names in LIST. To specify a main program in the LIST, give empty string.
  • Libraries specified by the environment variable <code>LD_PRELOAD</code> and then those listed in <code>/etc/ld.so.preload</code> are loaded before the search begins. A preload can thus be used to replace some (or all) of the requested library's normal functionalities, or it can simply be used to supply a library that would otherwise not be found.
  • Static libraries are searched and linked into the ELF file at link time and are not searched at run time.

Relative paths are supported by the special token <code>$ORIGIN</code>, which expands to the directory of the executable at runtime. For example, <code>-Wl,-rpath,$ORIGIN</code> would add the directory the executable is in to the rpath. This allows for building binaries that can be freely moved across different folders.

The role of GNU ld

The GNU Linker (GNU ld) implements a feature which it calls "new-dtags", which can be used to insert an rpath that has lower precedence than the <code>LD_LIBRARY_PATH</code> environment variable.

If the new-dtags feature is enabled in the linker (<code>--enable-new-dtags</code>), GNU <code>ld</code>, besides setting the <code>DT_RPATH</code> attribute, also sets the <code>DT_RUNPATH</code> attribute to the same string. At run time, if the dynamic linker finds a <code>DT_RUNPATH</code> attribute, it ignores the value of the <code>DT_RPATH</code> attribute, with the effect that <code>LD_LIBRARY_PATH</code> is checked first and the paths in the <code>DT_RUNPATH</code> attribute are only searched afterwards.

The ld dynamic linker does not search <code>DT_RUNPATH</code> locations for transitive dependencies, unlike <code>DT_RPATH</code>.

Instead of specifying the <code>-rpath</code> to the linker, the environment variable <code>LD_RUN_PATH</code> can be set to the same effect.

Solaris ld.so

The dynamic linker of Solaris, specifically <code>/lib/ld.so</code> of SunOS 5.8 and similar systems looks for libraries in the directories specified in the <code>LD_LIBRARY_PATH</code> variable before looking at the <code>DT_RPATH</code> attribute. Sun Microsystems was the first to introduce dynamic library loading. Sun later added the rpath option to ld and used it in essential libraries as an added security feature. GNU ld did the same to support Sun-style dynamic libraries.

<code>$ORIGIN</code> is supported.

In other dynamic linkers

ELF format:

  • musl's ld.so handles <code>DT_RUNPATH</code> in the same way as <code>DT_RPATH</code>. It uses the following order: <code>LD_LIBRARY_PATH</code>, then rpath/runpath, then default paths. It understands <code>$ORIGIN</code>.
  • bionic libc's ld.so does not recognize any form of rpath.
  • FreeBSD ld.so respects the non-inheritance and lower precedence of <code>DT_RUNPATH</code>. It also handles <code>$ORIGIN</code>.

Non-ELF formats:

  • The Mach-O format has a similar rpath system, specified via <code>LC_RPATH</code>. Unlike in ELF, the rpath must be explicitly referenced in the library list as <code>@rpath</code> for it to be considered in library search.

Security considerations

The use of rpath and also runpath can present security risks where the value applied includes directories under an attacker's control. This can include cases where the value defined explicitly references an attacker writable location but also instances where a relative path is used via <code>$ORIGIN</code>, or where a directory statement is left unpopulated. This can be leveraged to trick the binary into loading malicious libraries from one or other of the directories under an attacker's control. This is especially dangerous with setUID binaries, as these binaries run as a different (usually higher-permission-level, often root) user.

References

External links

  • chrpath - a tool to change the <code>DT_RPATH</code> attribute of an executable and convert it to an <code>DT_RUNPATH</code> attribute
  • FreeBSD devel/chrpath Port - Tool to modify DT_RPATH in existing ELF binaries
  • patchELF - a small utility to modify the dynamic linker and <code>DT_RUNPATH</code> attribute of ELF executables.