install_name_tool -change 和 -id 的区别

install_name_tool difference between -change and -id

我一直在为这个概念苦苦挣扎一段时间,我无法真正理解 -change-id 之间的区别。手册页指出

 -id name
              Changes  the  shared  library identification name of a dynamic shared library to name.  If the Mach-O binary is not a dynamic
              shared library and the -id option is specified it is ignored.

-change old new
              Changes  the dependent shared library install name old to new in the specified Mach-O binary.  More than one of these options
              can be specified.  If the Mach-O binary does not contain the old install name in a specified -change  option  the  option  is
              ignored.

到目前为止,我已经尝试了 -change。假设我有以下结构

Test.App
|_Contents
    |_MacOS
    |   |_test -----> item A
    |_Library
        |_test_library.dylib     --->item B
        |_another_library.dylib  --->item C

现在假设我 运行 itemB

上的以下内容
$ otool -L test_library.dylib
   test_library.dylib
   /some/path/another_library.dylib  -->item D

以上结果表明 test_library.dylib 依赖于 another_library.dylib 现在如果我需要更改 another_library.dylib 的位置我会这样做

install_name_tool -change /some/path/another_library.dylib some/new/path/another_library.dylib  test_library.dylib 

这会改变项目 D 的位置。我的问题是 install-name_tool -id 有什么作用以及我什么时候使用它?

安装名称

术语安装名称指的是最终用户系统中.dylib文件的确切路径,因此运行time链接器可以找到并加载动态库。

名称可以是:

  • 绝对,系统库就是这样。它们在最终用户和开发人员的系统中位于同一位置。
  • 相对,这是与应用程序捆绑在一起的库的情况。在最终用户的系统上,.dylib 将嵌入到应用程序包中,而在开发人员系统上,它们将预先构建在 /usr/local/opt/local 或其他地方,或者它们将作为应用构建的一部分从源构建。

后者是主要问题,因为在构建 .dylib 时,它的 安装名称 被链接器标记到 .dylib 中,这就是预计会在 运行 时间找到并加载它。显然这不会在最终用户系统上工作,因为该路径只存在于开发人员的系统上,所以解决方案是使用 install_name_tool 修改 install name库和引用这些库的可执行文件,将应用程序包放在一起时。

占位符

由于 executables/app 捆绑包可以安装在最终用户系统的不同位置,您可以使用占位符系统来抽象 安装名称 位置:

  • @executable_path:主要可执行文件的完整路径。
  • @loader_path:引用可执行文件的完整路径或.dylib
  • @rpath:主要可执行文件中设置的 RPATH。这也可以使用 install_name_tool.
  • 更改

因此,例如在 macOS 应用程序包中,可执行文件将位于 TheApp.app/Contents/MacOS/TheApp 中,库将位于 TheApp.app/Contents/Frameworks 中,因此您需要使用路径 @executable_path/../Frameworks/Library.dylib 引用库。

然而,最好将主要可执行文件的 RPATH 设置为 @executable_path/../Frameworks,并使用 @rpath/Library.dylib.

引用库

install_name_tool

install_name_tool有两个主要选项:

-id:设置 .dylib 文件本身的 安装名称 并将用作原型 安装名称 从那时起,当某些东西与 .dylib 链接时。您可以在构建 .dylib 后立即“更正” 安装名称,但这是一个不寻常的工作流程,因为图书馆如何知道使用它的环境?

-change:这会更改引用可执行文件(或 dylib)中 .dylib 安装名称

-id 名称与 -change 名称不匹配时会发生什么?没有什么。 -change 选项是正确的重要选项,因为一旦 运行 时间链接器找到 .dylib,任务就完成了。

xcodedevtools

您显然会使用脚本编写所有修复程序,但这有点乏味,所以我开发了 copy_dylibs.py 脚本来为您完成所有工作。在链接您的应用程序可执行文件后将其配置为 运行,它会查看您的可执行文件以递归查找 .dylib 文件以复制到应用程序包中。然后它修复了它们的 安装名称 ,留下原始的 .dylib 文件。

install_name_tool -id用于改变dylibinstall name,你可以使用otool -D在终端中看到一个dylib install name,它会为你显示默认值,/some/path/another_library.dylibanother_library.dylib的默认install name,当然你可以在终端中使用install_name_tool -id更改它,就像这样使用终端

install_name_tool -id /some/path/another_library_newname.dylib /some/path/another_library.dylib

现在,你使用otool -D /some/path/another_library.dylib,你会发现install name/some/path/another_library_newname.dylib

这是我在 picture

中的示例

id在link时使用,install name在运行时使用。它们都是为linker 定位 dylib 提供的信息。我关注了这个 tutorial.

让我举个例子,

$ cat a.cc
#include <iostream>
void a() { std::cout << "a()" << std::endl; }
$ clang++ -c a.cc
$ clang++ -o liba.dylib -dynamiclib a.o
$ otool -L liba.dylib
liba.dylib:
        liba.dylib (compatibility version 0.0.0, current version 0.0.0)
        /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)

如您所见,第一行是 id。让我们 link 和 libb.dylib,

$ cat b.cc
#include <iostream>
void a();
void b() { std::cout << "b()" << std::endl; a(); }
$ clang++ -c b.cc
$ clang++ -o libb.dylib -dynamiclib b.o -L. -la
$ otool -L libb.dylib
libb.dylib:
        libb.dylib (compatibility version 0.0.0, current version 0.0.0)
        liba.dylib (compatibility version 0.0.0, current version 0.0.0)
        /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)

请注意第二行,这里使用了 liba.dylib 的 id。让我们再次将 id 更改为 foo/liba.dylib 和 link,

$ install_name_tool -id foo/liba.dylib liba.dylib
$ otool -D liba.dylib
liba.dylib:
foo/liba.dylib
liba.dylib:
        foo/liba.dylib (compatibility version 0.0.0, current version 0.0.0)
        /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)

所以你看到 -D-L 都将当前 id 输出为 foo/liba.dylib

让我们再次 link 和 liba.dylib,

$ clang++ -o libb.dylib -dynamiclib b.o -L. -la
$ otool -L libb.dylib
libb.dylib:
        libb.dylib (compatibility version 0.0.0, current version 0.0.0)
        foo/liba.dylib (compatibility version 0.0.0, current version 0.0.0)
        /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 400.9.4)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1252.250.1)

看出区别了吗? 运行 查找 liba.dylib 的时间位置更改为 第二 行的 foo/liba.dylib

基本上,它告诉 libb.dylib 从 current_dir/foo

中找到 liba.dylib