CMake 项目无法找到共享库。

7
我正在使用CMake构建跨平台项目,目前我正在尝试在Linux上运行它。最近我添加了一个用于运行测试的项目,但是由于找不到其中一个共享库,特别是libtbbmalloc.so.2,所以它无法运行:
/tests: error while loading shared libraries: libtbbmalloc.so.2: cannot open shared object file: No such file or directory`

当我在可执行文件上运行ldd时,会得到以下结果:

linux-vdso.so.1 (0x00007fffeb572000)
libtbbmalloc_proxy.so.2 => /home/username/dev/tbb/libtbbmalloc_proxy.so.2 (0x00007f50afe00000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f50afa70000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f50af6d0000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f50af4b0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f50af0a0000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f50aee90000)
libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f50aec70000)
librt.so.1 => /lib/x86_64-linux-gnu/librt.so.1 (0x00007f50aea60000)
/lib64/ld-linux-x86-64.so.2 (0x00007f50b0400000)
libtbbmalloc.so.2 => not found

我的测试项目的CMakeLists.txt文件如下所示:

set(test_sourcefiles main_tests.cpp)

add_executable(tests ${test_sourcefiles})

target_link_libraries(tests Catch2::Catch2 MyLib)

MyLib使用了tbb,我猜这就是为什么我的可执行文件(tests)会搜索它。在运行ldd命令查看MyLib时,它找到了库(libtbbmalloc.so.2):

(removed some output for readability)
libtbbmalloc_proxy.so.2 => /home/username/dev/tbb/libtbbmalloc_proxy.so.2 (0x00007f9af8110000)
libtbbmalloc.so.2 => /home/username/dev/tbb/libtbbmalloc.so.2 (0x00007f9ac4eb0000)

我尝试在我的 tests/CMakeLists.txt 中特别添加了 libttbbmalloc.so.2,代码如下: target_link_libraries(${project} /home/username/dev/tbb/libtbbmalloc.so.2),但没有任何效果。

如果我将 /home/username/dev/tbb/ 添加到 LD_LIBRARY_PATH 中,程序可以运行,并且 ldd 报告找到了 libtbbmalloc.so.2。

你有什么想法是我做错了什么,以及如何让我的程序在不设置 LD_LIBRARY_PATH 的情况下运行?

更新:我发现可以使用 chrpath -l 可执行文件名称 打印 runpath/rpath。当我在可执行文件上使用该工具时,看起来添加了包含 libtbbmalloc.so.2 的文件夹到 runpath,但程序仍然无法运行:

larjr@DESKTOP:~/dev/project/build/tests$ chrpath -l tests
tests: RUNPATH=/home/larsjr/dev/project/build/MyLib:/home/username/dev/tbb

1
你所有的 ldd 命令都显示你的库已经正确解析(找到)。因此,如果你在相同的构建位置运行测试,它应该可以工作。@Zaffy 的回答假设你的问题是在程序安装之后。这是问题吗? - Former contributor
不,安装程序后问题并未发生。现在我尝试在构建位置运行它。第一个ldd命令的输出(可能应该强调一下)表明它无法解析libtbbmalloc.so.2。这是在构建测试项目时出现的问题吗?我不明白的是为什么一个名为MyLib的项目可以正常工作,而我的测试项目却不能。 - larsjr
2
你是对的,抱歉。你第一个ldd输出表明libtbbmalloc_proxy.so.2被正确解析,但libtbbmalloc.so.2没有被解析!这就解释了一切。你的程序链接到代理库,因此你的程序的RPATH用于定位代理库。但是真正的库没有链接到你的程序,而是链接到代理库。代理库没有嵌入RPATH来帮助链接器定位buddy。 - Former contributor
有道理。你有什么建议可以让代理库找到libtbbmalloc.so.2吗?我已经尝试在我的“tests”项目中显式链接它,但仍然无法找到它。 - larsjr
1
我会尝试两个选项:1:[patchelf](http://manpages.ubuntu.com/manpages/cosmic/man1/patchelf.1.html)将代理库插入 RPATH,或者2:在您的系统上安装库到某些标准前缀,如 /usr/local/ ,并将其包含在 /etc/ld.so.conf 中。 - Former contributor
3个回答

11

你遇到的问题似乎与共享库的直接和间接链接的搜索路径有关。CMake 可能会使用新的 ld RUNPATH 变量进行构建,该变量仅适用于直接依赖项,在所有间接依赖项上递归地工作的是 RPATH。如下面这篇文章底部的片段所示,这可能取决于您的 GCC 版本。要检查可执行文件是否使用 RPATH 或 RUNPATH,请运行:

$ readelf -d ./executable

在查找依赖项后,请查找库的rpath / runpath行。 例如:它可能会显示以下任一内容:

a) 0x000000000000001d (RUNPATH)            Library runpath: [/some/dir/lib]
b) 0x000000000000001d (RPATH)              Library rpath:   [/some/dir/lib]

你展示了使用命令chrpath -l ./executable的结果:

larjr@DESKTOP:~/dev/project/build/tests$ chrpath -l tests

tests: RUNPATH=/home/larsjr/dev/project/build/MyLib:/home/username/dev/tbb

这里显示了"RUNPATH",它是非递归的 - 这就是你的错误所在。

请参考这个优秀的github问题,其中成员正在讨论CMake间接链接问题,强烈建议阅读全部内容。它有一个关于依赖项及其搜索路径的很好的例子。 https://github.com/conan-io/conan/issues/2660

下面是关于新旧链接器行为的一段代码,可能会引起这个“问题”:

您在conan-deptest中遇到的问题(顺便说一句,这是一个绝佳的隔离案例!)是您正在记录的行为,在DT_RPATHDT_RUNPATH之间。 在构建可执行文件时,CMake(而不是conan)会传递-rpath标志以正确的路径传递给依赖库。 问题在于,某些平台上较新版本的链接器发出DT_RUNPATH,而不是以前的DT_RPATH。实际上,这是通过readelf -d读取RUNPATH标记的情况。

还有另一个stackoverflow帖子,其中用户询问LD/GCC中行为变化的问题: How to set RPATH and RUNPATH with GCC/LD?

Conan成员进一步阐述了如何更改此行为:

在发出RUNPATH而不是RPATH的链接器版本上复制此行为的方法是向链接器传递--disable-new-dtags标志。

下面是生成的cmake:

target_link_libraries(target "-Wl,--disable-new-dtags")

现在,如果你重新构建并在可执行文件上检查readelf -d,你应该会看到RPATH标签。你可以运行:

env LD_DEBUG=files,libs ./executable

查看RPATH是否传递到每个依赖项的搜索路径中:

31658:  file=libbar.so.1 [0];  needed by /home/user/projectA/lib/libfoo.so.2.5 [0]
31658:  find library=libbar.so.1 [0]; searching
31658:   search path=/home/user/projectA/lib        (RPATH from file ./executable)
31658:    trying file=/home/user/projectA/lib/libbar.so.1

希望这可以解决您的问题!

奖励: 链接到共享库教程。 文章底部展示了runpath与rpath + origin的比较! https://amir.rachum.com/blog/2016/09/17/shared-libraries/


3

我相信你想要在可执行文件上设置运行时路径:

set_target_properties(tests PROPERTIES
    INSTALL_RPATH "<your path to libs>"
    BUILD_WITH_INSTALL_RPATH 1
)

INSTALL_RPATH(安装路径)

通常情况下,当使用 RPATH 的系统构建可执行程序等时,CMake 会使用构建目录作为 RPATH 。当软件被安装后,CMake 会通过重新链接可执行程序等来使其具有安装的 RPATH。如果将此变量设置为 true,则软件始终使用安装路径作为 RPATH 进行构建,在安装时无需重新链接。

BUILD_WITH_INSTALL_RPATH(使用安装路径构建)

通常情况下,当使用 RPATH 的系统构建可执行程序等时,CMake 会使用构建目录作为 RPATH。当软件被安装后,CMake会通过重新链接可执行程序等使其具有安装的 RPATH。如果将此变量设置为 true,则软件始终使用安装路径作为RPATH进行构建,在安装时无需重新链接。


这不幸的是没有解决我的问题。我仍然收到相同的错误消息。我找到了一个打印rpath的命令,并且看起来正确的文件夹已经添加到路径中,但它仍然无法找到库。奇怪的是,似乎正确地将正确的文件夹添加到路径中,而不需要添加您建议的命令,但它仍然无法工作。我会更新我的问题并提供细节。 - larsjr
@larsjr,你能否请运行strace在你的可执行文件上吗? - Zaffy
我已经运行了strace,但我不熟悉这个工具,所以输出还不能告诉我太多信息。我在这里上传了它:https://gist.github.com/larsjr/673ce1eb1887b132745f74be95925264 请注意,在我的原始问题中,我简化了一些路径。包含libtbbmalloc.so.2文件夹的位置是/home/lars/dev/mi-mil/build/mimil-build/packages/icgi-intelTBB.2019.8.281.1/build/native/lib/linuxx64/Release - larsjr
2
@larsjr strace钩住你的程序并显示它所做的所有系统调用。这在你的情况下非常有用,因为你可以看到链接器正在查找库的位置。看起来tbbmalloc_proxy被找到了,但是lbbmalloc甚至没有在你的rpath中搜索。这可能意味着tbbmalloc_proxy尝试动态加载其他tbbmalloc?你能否为我制作一个“hello world”程序以重现此问题? - Zaffy
抱歉,我最近几天没能工作。我尝试创建一个“hello world”的例子,但是我无法重现错误。似乎在简化过程中,我丢失了一些关键部分。如果有用的话,示例已上传至此处:https://github.com/larsjr/tbb_link_example(在运行ldd命令时,libproject1.so被报告为“静态链接库”)。 - larsjr

0

在可执行文件安装命令旁边添加set_target_properties


# install
INSTALL(TARGETS ${PROJECT_NAME} myLib
    RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
    LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
)

set_target_properties(${PROJECT_NAME} PROPERTIES
    INSTALL_RPATH ${CMAKE_INSTALL_PREFIX}/lib
    BUILD_WITH_INSTALL_RPATH ON
)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接