This guide explains RPATH implementation in this C++ project template, including configuration, usage patterns, and troubleshooting.
RPATH (Runtime Path) specifies library search paths embedded directly in executable files. This mechanism enables the dynamic linker to locate shared libraries at runtime without relying on system-wide configuration or environment variables.
When executing a binary, the dynamic linker searches for shared libraries in the following order:
- Directories in LD_LIBRARY_PATH environment variable
- System directories (/lib, /usr/lib, /usr/local/lib)
- Paths specified in /etc/ld.so.conf
Project-specific shared libraries located outside standard system directories (e.g., ../lib relative to the executable) are not automatically discovered.
# Approach 1: Environment variable configuration
export LD_LIBRARY_PATH=/path/to/lib:$LD_LIBRARY_PATH
./main_exec
# Approach 2: System directory installation
sudo cp lib*.so /usr/local/lib/
# Approach 3: Absolute path specification
dlopen("/absolute/path/to/plugin.so", RTLD_LAZY);Limitations:
- Approach 1: Fragile, requires per-session configuration
- Approach 2: Pollutes system directories, requires administrative privileges
- Approach 3: Non-portable, breaks when installation paths change
RPATH embeds library search paths directly in the executable, creating self-contained, portable binaries that function independently of environment configuration.
$ORIGIN: Directory containing the executable (resolved at runtime)$LIB: Platform-specific library directory (e.g., lib64 on some systems)
# Libraries in same directory as executable
set(CMAKE_INSTALL_RPATH "$ORIGIN")
# Libraries in ../lib relative to executable
set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")
# Multiple search paths
set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib:$ORIGIN")package/
├── bin/
│ └── main_exec (RPATH: $ORIGIN/../lib)
└── lib/
├── libexample_shared.so (Linked at compile time)
└── libexample_plugin_impl.so (Loaded at runtime via dlopen)
# In src/main/CMakeLists.txt
set_target_properties(main_exec PROPERTIES
# Development builds (relative to build directory)
BUILD_RPATH "${CMAKE_BINARY_DIR}/lib"
# Installed builds (relative to installation directory)
INSTALL_RPATH "$ORIGIN/../lib"
# Use BUILD_RPATH during development
BUILD_WITH_INSTALL_RPATH OFF
)// In main.cpp
#include "example_shared.hpp"
int main() {
example_shared::greet();
}Execution sequence:
- Dynamic linker reads RPATH from executable:
$ORIGIN/../lib - Resolves
$ORIGINto/path/to/package/bin - Searches for
libexample_shared.soin/path/to/package/lib - Loads library
// In plugin_loader.cpp
void* handle = dlopen("libexample_plugin_impl.so", RTLD_LAZY);Execution sequence:
- dlopen respects RPATH from calling executable
- Searches in
$ORIGIN/../lib=/path/to/package/lib - Loads
libexample_plugin_impl.so
build/
├── main_exec (BUILD_RPATH: ./lib)
├── lib/
│ ├── libexample_shared.so
│ └── libexample_plugin_impl.so (Copied by CMake)
└── src/example_plugin_impl/
└── libexample_plugin_impl.so (Original location)
install/
├── bin/
│ └── main_exec (INSTALL_RPATH: $ORIGIN/../lib)
└── lib/
├── libexample_shared.so
└── libexample_plugin_impl.so
readelf -d build/main_exec | grep -E "(RPATH|RUNPATH)"or
objdump -x build/main_exec | grep -E "(RPATH|RUNPATH)"Expected output:
0x000000000000000f (RPATH) Library rpath: [$ORIGIN/../lib]
ldd build/main_execLD_DEBUG=libs ./build/main_exec 2>&1 | grep -E "(search|trying)"set(CMAKE_INSTALL_RPATH "$ORIGIN")package/
├── app_exec
├── libfoo.so
└── libbar.so
set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib")package/
├── bin/app_exec
└── lib/
├── libfoo.so
└── libbar.so
set(CMAKE_INSTALL_RPATH "$ORIGIN/../lib:$ORIGIN/../plugins:$ORIGIN")package/
├── bin/app_exec
├── lib/libcore.so
├── plugins/libplugin.so
└── bin/libutils.so
- Use
$ORIGINfor portable, relocatable packages - Configure both
BUILD_RPATHandINSTALL_RPATH - Test packages across different systems
- Prefer relative paths in RPATH
- Hardcoding absolute paths in RPATH
- Relying on
LD_LIBRARY_PATHfor production deployments - Installing libraries to system directories
- Using RPATH for system libraries (already in standard locations)
error while loading shared libraries: libexample_shared.so: cannot open shared object file
Diagnostic steps:
- Verify RPATH:
readelf -d main_exec | grep RPATH - Confirm library exists:
ls -la lib/libexample_shared.so - Check permissions:
file lib/libexample_shared.so - Trace loading:
LD_DEBUG=libs ./main_exec
void* handle = dlopen("libplugin.so", RTLD_LAZY);
if (!handle) {
std::cerr << "dlopen error: " << dlerror() << std::endl;
}Diagnostic steps:
- Verify plugin presence in RPATH directories
- Check plugin dependencies:
ldd lib/libplugin.so - Verify exported symbols:
nm -D lib/libplugin.so