如何仅用target_link_directories为共享库设置RPATH(无需target_link_libraries)
项目目标
需求是从主可执行文件加载libfoo.so,而libfoo.so需要加载libbar.so。调用dlopen时不想指定路径,只使用dlopen("libfoo.so", RTLD_LAZY)这类写法。已知libdl查找共享库的方式包括:
- 环境变量
LD_LIBRARY_PATH的值 - 二进制文件中嵌入的RPATH
- libdl默认的标准目录
项目目录结构
. ├── CMakeLists.txt ├── build # 用于执行`cmake ..`进行源码外构建 ├── libs │ ├── CMakeLists.txt │ ├── bar.c │ └── foo.c └── main.c
现有实现情况
主程序main.c通过在对应CMakeLists.txt中添加target_link_directories(main PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/libs),成功实现dlopen("libfoo.so", RTLD_LAZY),且main可执行文件已嵌入预期RPATH:
$ objdump -x ./main | grep PATH RUNPATH /home/user/code/scratch/3/build/libs
但libfoo.so调用dlopen("libbar.so", RTLD_LAZY)失败,尽管在其CMakeLists.txt中添加了target_link_directories(foo PRIVATE ${CMAKE_CURRENT_BINARY_DIR}),libfoo.so中并未生成RPATH:
$ objdump -x ./libs/libfoo.so | grep PATH $
已尝试的方案
测试发现只有当共享库至少有一条target_link_libraries语句时,target_link_directories才会为其添加对应的RPATH:
- 将
libfoo.so与libbar.so关联:
# 该方式有效,但希望避免 target_link_libraries(foo bar)
执行后libfoo.so生成预期RPATH:
$ objdump -x ./libs/libfoo.so | grep PATH RUNPATH /home/user/code/scratch/3/build/libs
- 将
libfoo.so与非必需库(如dl)关联:
# 关联非必需库后,target_link_directories生效 target_link_libraries(foo dl) target_link_directories(foo PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
执行后同样生成预期RPATH。
疑问
- 我的理解是否正确:仅当共享库存在至少一条
target_link_libraries语句时,target_link_directories才会为其添加对应的RPATH?如果正确,请解释设计原理。 - 是否存在更简洁的方式,无需添加不必要的
target_link_libraries语句,仅通过CMake配置为共享库添加RPATH?
代码文件
main.c
#include <dlfcn.h> #include <stdlib.h> #include <stdio.h> int main(int argc, char* argv[]) { void* handle = dlopen("libfoo.so", RTLD_LAZY); if (!handle) { printf("dlopen error: %s\n", dlerror()); return EXIT_FAILURE; } { void (*fptr)() = dlsym(handle, "func"); if (fptr) { fptr(); } } dlclose(handle); return EXIT_SUCCESS; }
libs/foo.c
#include <dlfcn.h> #include <stdio.h> void func() { void* handle = dlopen("libbar.so", RTLD_LAZY); printf("here in libfoo!\n"); if (!handle) { printf("dlopen error: %s\n", dlerror()); return; } { void (*fptr)() = dlsym(handle, "func"); if (fptr) { fptr(); } } dlclose(handle); }
libs/bar.c
#include <stdio.h> void func() { printf("here in libbar!\n"); }
根目录CMakeLists.txt
cmake_minimum_required(VERSION 3.16) project(my_prj) add_subdirectory(libs) add_executable(main main.c) target_link_libraries(main dl) target_link_directories(main PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/libs)
libs/CMakeLists.txt
add_library(bar SHARED bar.c) add_library(foo SHARED foo.c) # 期望的方式,但无法生效: target_link_directories(foo PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) # 关联libbar有效,但希望避免: # target_link_libraries(foo bar) # 关联不必要的库后生效: # target_link_libraries(foo dl) # target_link_directories(foo PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
解答
问题1解答
你的理解完全正确。CMake的设计逻辑是:target_link_directories的核心作用是为链接阶段提供查找依赖库的路径。如果目标没有通过target_link_libraries声明任何链接依赖,CMake会默认认为该目标不需要在运行时查找额外的库,因此不会将target_link_directories指定的路径转化为RPATH嵌入到目标文件中。只有当存在链接依赖时,CMake才会生成RPATH,确保运行时能找到这些依赖库。
问题2解答
存在更简洁的方式,无需添加不必要的链接库,直接通过set_target_properties手动设置RPATH即可:
修改libs/CMakeLists.txt中关于foo的配置,替换原本的target_link_directories语句为:
set_target_properties(foo PROPERTIES BUILD_RPATH "${CMAKE_CURRENT_BINARY_DIR}" )
BUILD_RPATH会为构建阶段生成的共享库指定RPATH,满足本地运行时的查找需求;- 如果后续需要支持安装后的RPATH,可以额外设置
INSTALL_RPATH属性。
这样配置后,libfoo.so会正确嵌入指定的RPATH,无需添加任何不必要的target_link_libraries语句。
内容的提问来源于stack exchange,提问作者StoneThrow

