install_name_tool更新可执行文件以在Mac OS X中搜索dylib

pro*_*eek 38 macos path dylib ld dyld

我有一个安装在其中的动态libray libtest.dylib /PATH/lib,以及一个使用安装的dylib的执行二进制文件myapp /PATH/bin.

我可以myapp按如下方式运行以找到dylib(在Mac OS X上使用DYLD_LIBRARY_PATH是否可以?并且,使用它的动态库搜索算法是什么?):

DYLD_LIBRARY_PATH="/PATH/lib" myapp 
Run Code Online (Sandbox Code Playgroud)

我想我可以install_name_tool用来更新库和可执行文件,以便可以使用rpath找到库.我在这篇文章中使用了提示 - 如何在dylib中指定rpath?.

在lib中,我执行了这个命令来添加rpath.

install_name_tool -id "@rpath/libtest.dylib" libtest.dylib
install_name_tool -add_rpath "@executable_path/../lib/" libtest.dylib
Run Code Online (Sandbox Code Playgroud)

在bin中,我执行了install_name_tool -add_rpath "@executable_path/../lib/" myapp.

但是,当我myappbin目录中执行时,我有错误消息.

dyld: Library not loaded: libtest.dylib
  Referenced from: /PATH/bin/./myapp
  Reason: image not found
Trace/BPT trap: 5
Run Code Online (Sandbox Code Playgroud)

otool -l myapp 显示rpath在myapp中正确更新.

Load command 16
          cmd LC_RPATH
      cmdsize 40
         path @executable_path/../lib/ (offset 12)
Run Code Online (Sandbox Code Playgroud)

libtest.dylib也是如此

Load command 13
          cmd LC_RPATH
      cmdsize 40
         path @executable_path/../lib/ (offset 12)
Run Code Online (Sandbox Code Playgroud)

可能有什么问题?

添加

当然,我可以cc -install_name在编译和链接时使用,但我想知道如何做同样的事情我修改generatd dylib和执行二进制文件.

来自lib:

cc -install_name "@loader_path/../lib/libtest.dylib" -dynamiclib -o libtest.dylib test.c
Run Code Online (Sandbox Code Playgroud)

或者,install_name可以使用@rpath:

cc -install_name "@rpath/libtest.dylib" -dynamiclib -o libtest.dylib test.c
Run Code Online (Sandbox Code Playgroud)

从垃圾箱:

cc -I../lib -c main.c
cc -o main main.o ../lib/libtest.dylib -Wl,-rpath -Wl,@loader_path/../lib
Run Code Online (Sandbox Code Playgroud)

或者只是一行:

cc -I../lib -L../lib -o main main.c -ltest -Wl,-rpath -Wl,@loader_path/../lib
Run Code Online (Sandbox Code Playgroud)

pro*_*eek 39

otool -l,我分析了应该从原始库和二进制文件中添加或修改的内容.

Dylib

更改是在id:

Load command 2 <-- OLD
          cmd LC_ID_DYLIB
      cmdsize 40
         name libtest.dylib (offset 24)
   time stamp 1 Wed Dec 31 18:00:01 1969

Load command 2 <-- NEW
          cmd LC_ID_DYLIB
      cmdsize 64
         name @loader_path/../lib/libtest.dylib (offset 24)
Run Code Online (Sandbox Code Playgroud)

这是完成更改的命令:

install_name_tool -id "@loader_path/../lib/libtest.dylib" libtest.dylib 
Run Code Online (Sandbox Code Playgroud)

或者使用rpath:

install_name_tool -id "@rpath/libtest.dylib" libtest.dylib
Run Code Online (Sandbox Code Playgroud)

可执行文件

有两个变化:rpath和load_dylib

Load command 12 <-- OLD
          cmd LC_LOAD_DYLIB
      cmdsize 40
         name libtest.dylib (offset 24)

Load command 12 <-- NEW
          cmd LC_LOAD_DYLIB
      cmdsize 64
         name @loader_path/../lib/libtest.dylib (offset 24)
Run Code Online (Sandbox Code Playgroud)

这是完成更改的命令

install_name_tool -change libtest.dylib @loader_path/../lib/libtest.dylib myapp 
Run Code Online (Sandbox Code Playgroud)

我还需要添加rpath

Load command 14
          cmd LC_RPATH
      cmdsize 32
         path @loader_path/../lib (offset 12)
Run Code Online (Sandbox Code Playgroud)

这是完成添加的命令:

 install_name_tool -add_rpath "@loader_path/../lib" myapp
Run Code Online (Sandbox Code Playgroud)

这个想法

二进制文件尝试查找库,它知道它的位置install_name_tool -add_rpath "@loader_path/../lib" myapp.它加载库,并且库的id是@rpath/libtest.dylib在可执行二进制文件中@rpath设置的位置@loader_path/../lib以进行匹配.

参考

cmake的

使用CMake时,我们可以通过在CMakeLists.txt文件中添加以下内容来自动化该过程.

图书馆

应该添加id.

# https://cmake.org/pipermail/cmake/2006-October/011530.html
SET_TARGET_PROPERTIES (test
  PROPERTIES BUILD_WITH_INSTALL_RPATH 1
             INSTALL_NAME_DIR "@rpath"
  )
Run Code Online (Sandbox Code Playgroud) 可执行文件

应该指定rpath:

SET(CMAKE_INSTALL_RPATH "@loader_path/../lib/libtest.dylib")
Run Code Online (Sandbox Code Playgroud)

  • 在运行时,dylib中的属性似乎并不重要。它们仅在构建期间用于将属性从dylib复制到可执行文件。对于已经具有链接到dylib的可执行文件但需要更改路径的情况,仅编辑可执行文件以设置LC_LOAD_DYLIB以及可选的LC_RPATH就足够了。仅当第一个中包含@rpath时,才需要第二个。 (3认同)