【问题标题】:CMake: Run-time error (dyld: Library not loaded) for dynamically linked resources on MacOSCMake:MacOS上动态链接资源的运行时错误(dyld:未加载库)
【发布时间】:2018-05-21 17:16:29
【问题描述】:

问题

在 MacOS 上,我在运行时遇到依赖于动态链接资源的 CMake 项目的链接问题 - 但仅在安装项目之后!当我只构建二进制文件而不安装它时,不会出现此问题。

$ ./testapp
Hello world!
$ $INSTALLDIR/testapp
dyld: Library not loaded: @rpath/libvtkDomainsChemistryOpenGL2-7.1.1.dylib
  Referenced from: /Users/normanius/workspace/installdir/testapp
  Reason: image not found
[1]    76964 trace trap  /Users/normanius/workspace/installdir/testapp   

最小示例

我能够在由CMakeLists.txtmain.cpp 组成的最小设置中重现该问题。我要链接的库名为 VTK (v7.1.1),它是使用共享库构建的(有关详细信息,请参见下文)。

# CMakeLists.txt
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
project(test)

# Test application.
add_executable(testapp
               main.cpp)

# Find vtk (library that has to be linked to dynamically).
find_package(VTK REQUIRED)
include(${VTK_USE_FILE})
target_link_libraries(testapp ${VTK_LIBRARIES}) # <---- this causes the problem

# Install instructions.
install(TARGETS testapp DESTINATION "${CMAKE_INSTALL_PREFIX}")

main.cpp 甚至不使用任何 VTK 对象。

// main.cpp
#include <iostream>

int main (int argc, char* argv[])
{
    std::cout << "Hello world!" << std::endl;
    return 0;
}

我使用以下命令构建项目。我设置的标志CMAKE_PREFIX_PATH 是为了给 CMake 一个关于在哪里可以找到 VTK 库的提示。

$ INSTALLDIR="path/to/installation"
$ mkdir build && cd build
$ cmake .. -DCMAKE_PREFIX_PATH="$DEVPATH/lib/vtk/cmake" \
           -DCMAKE_BUILD_TYPE=Release \
           -DCMAKE_INSTALL_PREFIX="$INSTALLDIR" 
$ make
$ make install

在构建文件夹中执行testapp 时,一切正常:

$ ./testapp
Hello world!
$ cp testapp $INSTALLDIR/testapp
$ $INSTALLDIR/testapp
Hello world!

但是,如果我在 INSTALLDIR 中运行可执行文件,我会收到运行时错误:

$ $INSTALLDIR/testapp
dyld: Library not loaded: @rpath/libvtkDomainsChemistryOpenGL2-7.1.1.dylib
  Referenced from: /Users/normanius/workspace/installdir/testapp
  Reason: image not found
[1]    76964 trace trap  /Users/normanius/workspace/installdir/testapp    

当然,如果我删除 CMakeLists.txt 中的 target_link_libraries() 指令,问题就会消失。

那么在安装 CMake 项目时究竟会发生什么?我的情况出了什么问题?我测试了不同的 CMake 版本(3.5、3.9 和 3.10)——但行为是相同的。

详情

显然,MacOS 上的 RPATH 机制没有为示例正确设置。

这是testapp二进制文件的链接结构的摘录:

$ otool -L testapp
testapp:
    @rpath/libvtkDomainsChemistryOpenGL2-7.1.1.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libvtkFiltersFlowPaths-7.1.1.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libvtkFiltersGeneric-7.1.1.dylib (compatibility version 0.0.0, current version 0.0.0)
    @rpath/libvtkFiltersHyperTree-7.1.1.dylib (compatibility version 0.0.0, current version 0.0.0)
...

因为它可能会影响 VTK 库(另一个 CMake 项目)的构建方式:对于 python 支持,必须设置项目标志 VTK_WRAP_PYTHON=ONBUILD_SHARED_LIBS=ON。安装前缀设置为CMAKE_INSTALL_PREFIX="$VTK_INSTALL_DIR"。为了确保在运行时找到资源,还必须通过CMAKE_MACOSX_RPATH=ONCMAKE_INSTALL_RPATH="$VTK_INSTALL_DIR/lib" 启用 RPATH 支持。

总结

我在概念上会犯什么错误?使用make install 安装项目时会发生什么?这个问题可以在 CMake 中解决吗?还是仅与 VTK 以及共享库的构建方式有关?

【问题讨论】:

    标签: macos cmake shared-libraries vtk dylib


    【解决方案1】:

    CMake 在运行 make install 时更改所有已安装目标的 RPATH。

    想象一下,将共享库和可执行文件构建为同一个 CMake 项目的一部分。为了能够运行可执行文件,它必须能够在运行时动态加载共享库。因此,默认情况下,CMake 会将 构建树中的动态库的完整(绝对)路径添加到可执行文件的 rpath。这对于开发来说非常方便,因为我们可以直接从构建树运行可执行文件,但我们可能不希望以这种方式发布可执行文件。

    这就是为什么 CMake 会在安装时将 rpath 更改为仅包含 portable 路径(即删除指向构建树的条目)。也就是说,除非您将共享库放入系统默认位置之一,否则可执行文件在安装后将找不到它。

    CMake 确实允许您指定一个安装 rpath,该路径将用您指定的构建树条目替换已删除的构建树条目。有关详细信息,请参阅 INSTALL_RPATHINSTALL_RPATH_USE_LINK_PATH 目标属性。

    由于所有这些 rpath 内容都是 100% 依赖于平台的,因此 OSX 带有自己的特殊规则。可以在(不幸的是相当过时的)CMake wiki 上找到非常全面的解释:

    与其他 UNIX 不同,达尔文链接器 dyld 定位依赖 使用每个 dylib 的完整路径的动态库。例如,在 可执行文件“foo”,记录的完整路径是安装名称 每个依赖的dylib。库“/usr/lib/libSystem.dylib”有 由“otool”给出的“/usr/lib/libSystem.B.dylib”安装名称 -D"。当链接到 "foo" 时,"foo" 对 "/usr/lib/libSystem.B.dylib" 有依赖关系。这种依赖关系可以通过 "otool" 看到 -L foo"。对于可重定位的二进制文件,可以使用@executable_path、@loader_path 和@rpath。在“foo”示例中,@executable_path 和 @loader_path 被替换为“foo”的位置。 @rpath 是 用“foo”中的 RPATH 替换以定位依赖的 dylib。因此 RPATH 机制开始发挥作用。链接器将搜索 @rpath/ 依赖项按以下顺序:

    • DYLD_LIBRARY_PATH - 一个 包含目录列表的环境变量
    • RPATH - 一个列表 链接到可执行文件的目录。这些可以包含 @loader_path 和 @executable_path。
    • 内置目录 - /lib /usr/lib
    • DYLD_FALLBACK_LIBRARY_PATH - 一个环境变量,它包含一个 目录列表

    您应该能够通过调整各自的目标属性来解决这个问题,但它相当繁琐,而且要正确处理可能会很痛苦。

    【讨论】:

    • 多么棒的答案。太感谢了!!!最后我明白这是如何工作的。确实,如果我在命令cmake命令中添加标志-DCMAKE_INSTALL_RPATH_USE_LINK_PATH="ON",问题就解决了!
    • 我完全同意共享库的链接有点繁琐。在 Mac OS ElCapitan 之前,我通常依赖 DYLD_LIBRARY_PATH 技巧,但这种“hack”并不总是有效,因为 Apple introduced the System Integrity Protection 是我在 an earlier SO post 中处理的一个问题
    【解决方案2】:

    这为我解决了这个问题:

    set(CMAKE_MACOSX_RPATH OFF)
    
    add_library(your-lib SHARED)
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-07-25
      • 2018-02-25
      • 2018-08-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多