GNU Dynamic Loader search directories

The GNU dynamic loader┬áis one of the main components of the user space in Linux based systems (file name: /lib/ld-linux.so.2). Whenever a program is executed, the dynamic loader is loaded into the process’ address space and called by the kernel before the control is passed to the program’s “main” function (basically, it is not the “main” function initially called by the kernel, but this knowledge is sufficient for a high-level understanding and is not part of the dynamic loader internals). The main task of the dynamic loader is to handle the interaction between the program and the system’s shared libraries by relocating unresolved symbols. To keep the programs as portable as possible across different GNU/Linux based systems, the program usually only records the shared library name (in fact the shared library’s internal soname to enable library versioning) while omitting the absolute path to the shared library. Before relocating unresolved symbols, the dynamic loader needs to find the appropriate shared library by searching different directories which is the focus of this short and high-leveled post.

The man page ld.so(8) [1] serves thereby as an entry point which directories are searched in which order:

  1. the DT_RPATH value of the program’s .dynamic ELF section (colon separated list of directories)
  2. the LD_LIBRARY_PATH environment variables (colon separated list of directories)
  3. the DT_RUNPATH value of the program’s .dynamic ELF section (colon separated list of directories)
  4. the dynamic loader cache file, usually /etc/ld.so.cache
  5. the default system library directories configured at compile-time, usually /lib and /usr/lib (skipped if the binary is linked with -z nodefaultlib)

(Note: the LD_PRELOAD environment variable could be used to specify shared libraries to loaded before any other shared libraries, but you need to specify the absolute library path instead of just a search directory as in the cases above)

Search steps 2, 4 and 5 are easy ignoring the very details, but how to set the DT_RPATH and DT_RUNPATH value of the .dynamic ELF section? For demonstration purpose, assume the following simple test program (main.c) and two shared libraries source files (lib.c and alt/lib.c) as reference for the subsequent examples. The base directory is ~/rpath_example.

Shared library source files lib.c and alt/lib.c:

Refer to this page for a GCC shared library tutorial. The following commands will finally create the shared libraries libtest.so and alt/libtest.so:

Common Use

The common way to link the main program against the shared library libtest.so would require the environment variable LD_LIBRARY_PATH to be set:

The .dynamic ELF section of the main program then yields:

DT_RPATH

The DT_RPATH feature allows you to embed the search path for the dynamic loader into the executable:

The $ORIGIN variable within the DT_RPATH value refers to the current execution directory of the main program (which means that $ORIGIN is equal to “.”). As an alternative to the linker option -rpath, you could also set the LD_RUN_PATH environment variable.

The general drawback using DT_RPATH is that you cannot overwrite this setting with LD_LIBRARY_PATH which limits the flexibity of using shared libraries (the only way to get around this limitation is to remove the shared library from the specified paths if you want to avoid the re-compilation of the application). This drawback is solved by DT_RUNPATH.

DT_RUNPATH

The DT_RPATH limitation is solved by the dynamic loader by ignoring the DT_RPATH value if the DT_RUNPATH value is set. In this case, the dynamic loader searches the LD_LIBRARY_PATH before the embedded path in the executable. The DT_RUNPATH value is set with the linker options -rpath (or LD_RUN_PATH) and the –enable-new-dtags.

References
[1] http://linux.die.net/man/8/ld-linux

Leave a Reply

Your email address will not be published. Required fields are marked *