You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何仅用target_link_directories为共享库设置RPATH(无需target_link_libraries)

关于CMake中共享库RPATH设置的问题

项目目标

需求是从主可执行文件加载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:

  1. 将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
  1. 将libfoo.so与非必需库(如dl)关联:
# 关联非必需库后,target_link_directories生效
target_link_libraries(foo dl)
target_link_directories(foo PRIVATE ${CMAKE_CURRENT_BINARY_DIR})

执行后同样生成预期RPATH。

疑问

  1. 我的理解是否正确:仅当共享库存在至少一条target_link_libraries语句时,target_link_directories才会为其添加对应的RPATH?如果正确,请解释设计原理。
  2. 是否存在更简洁的方式,无需添加不必要的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.23 14:03:22