Understanding RPATH (With CMake)

It may very well be that this is common knowledge among Linux enthusiasts, but for me, as a Windows user, it took quite a while to fully understand the concept of RPATH (Run-time Search Path). For those in the same boat, I want to share some of my newfound knowledge and of course also link to the resources which helped me better understand this topic.

The Problem

When you link a shared library (*.so on Linux or *.dylib on macOS), your executable needs to somehow know, where to look for said library at runtime. In most cases the library would be placed in a common system library path and the executable would find it due to a predefined list of places to search. However, in case you want to ship the shared library file with your executable or have multiple versions installed in parallel, you need to make sure, that the library can be found at your custom location.

With RPATH you can solve this issue by embedding additional search paths directly into the executable. Unfortunately, the concept gets a bit more tricky, as there are some differences between Linux and macOS.

Path weaving through lush landscape with a village in the far distance and clear blue sky

Relativity

In a lot of cases and for maximum portability, you don’t want to specify an absolute path to the shared library, but have it relative to the executable. For that you get $ORIGIN on Linux and @rpath / @loader_path / @executable_path on macOS.

Linux

On Linux $ORIGIN represents at runtime the location of the executable and thus if set the RPATH to for example $ORIGIN/lib/libmylib.so, your executable will look for the needed shared library relative to the executable location.

macOS

On macOS @rpath is prepended to the library path (at least when using CMake), so that when you set your custom path as RPATH, @rpath is replaced with your own path. However macOS also provides two additional placeholders: @loader_path and @executable_path. As an example if the executable depends on @rpath/libmylib.dylib and you set the RPATH to @loader_path/lib, the final search path will be @loader_path/lib/libmylib.dylib fulfilling the same goal as described above for Linux.

CMake Magic

After some theory let’s see how this works in practice with CMake, the defacto standard build system these days. Two general point upfront:

  • To prevent old behaviors and having to deal with CMake policies, you should use CMake version >3 and judging by Repology, I would recommend a minimum of CMake 3.13.
  • RPATH can also be useful during development, as you can link libraries within the build tree relative to the executable. CMake offers quite a few options to refine the behavior during build tree linking and install linking, as you will see below.

The Variables

I won’t go into details about the following list of CMake variables related to RPATH. If you want to know more, you best check out the linked documentation or resource links at the end.

Default Behavior

By default RPATH will be used in the build tree, but cleared when installing the targets, leaving an empty RPATH. When MACOSX_RPATH is set on macOS, then the install names for dylibs will include @rpath/ as prefix.

Always Full RPATH

With the following settings you can ensure that the libraries will be found in the build tree as well when you install the targets. Note: Don’t forget to set MACOSX_RPATH on macOS.

# use, i.e. don't skip the full RPATH for the build tree
set(CMAKE_SKIP_BUILD_RPATH FALSE)

# when building, don't use the install RPATH already
# (but later on when installing)
set(CMAKE_BUILD_WITH_INSTALL_RPATH FALSE)
set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib")

# add the automatically determined parts of the RPATH
# which point to directories outside the build tree to the install RPATH
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)

For more details on the interaction of these commands check out the CMake RPATH Handling wiki page.

No RPATH

You can set CMAKE_SKIP_RPATH to completely ignore RPATH all together.

What About Windows?

The question “How can I put the DLLs in a sub-directory?” gets asked often, unfortunately, Windows doesn’t provide a solution to this, meaning Windows doesn’t support the concept of RPATH at all. Among other things, this is also the reason why a lot of developers prefer static linking on Windows.

Resources

7 thoughts on “Understanding RPATH (With CMake)

  1. Great! This totally solved my problem.

    I was running a simulation program with GEANT4 where CMake is used to build the executable. It works from the build directory but “cannot open shared object file” after installed. This can be fixed by adding

    set(CMAKE_INSTALL_RPATH_USE_LINK_PATH FALSE)

    in CMakeLists.txt.

    Thanks a lot!

  2. Excellent post. But I have one strange problem when I use cpack -G DEB all rpaths are gone. Do you know how to keep all rpath to be relative to bin after pack. Thanks.

    ldd myapp
    mylib.so => not found

    1. I does indeed look like one can achieve this with a manifest, even if the right documentation is hard to come by.

Leave a Comment

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

 

This site uses Akismet to reduce spam. Learn how your comment data is processed.