install_name_tool中-change和-id的区别

13

我已经苦苦思索这个概念一段时间了,但我仍然不能真正理解-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 是什么,并且我什么时候使用它?

3个回答

17

安装名称

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

名称可以是以下两种情况之一:

  • 绝对路径,在系统库的情况下是如此。这些在最终用户系统和开发者系统上都放置在相同的位置。
  • 相对路径,在应用程序捆绑的库的情况下是如此。在最终用户系统上,.dylib将嵌入在应用程序捆绑包中;而在开发者系统上,它们将预构建在/usr/local/opt/local或其他某个位置,或作为应用程序构建的一部分从源代码构建。

后者是主要问题,因为当构建.dylib时,链接器会将其安装名称打印到.dylib中,该名称被认为是在运行时从中找到和加载的路径。显然,这在最终用户系统上不起作用,因为该路径只存在于开发者系统上,所以解决方案是在组合应用程序捆绑包时使用install_name_tool修改库和引用这些库的可执行文件的安装名称

占位符

由于可执行文件/应用程序捆绑包可能安装在最终用户系统上的不同位置,因此可以使用占位符系统来抽象安装名称的位置:

  • @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名称与磁盘上的.dylib位置不匹配时会发生什么?没有什么。 -change选项是正确的,因为一旦运行时链接器找到.dylib,那么任务就完成了。

为什么要有一个-id名称?如果链接器被告知在dirAdirBdirC(例如使用-L)中查找库,并且它找到正在寻找的库,那么为什么库本身必须将其-id标记到其中?毫无头绪;这可能是一些古老的无聊的东西。

xcodedevtools

显然,您会将所有这些修复作为构建过程的一部分进行脚本编写,我已经完成了这个工作并将其发布在这里。请参阅README.md获取有关如何在Xcode中配置它的详细信息。


你是说当我们在/Contents/MacOS/内更改主可执行文件时,使用-id吗? - MistyD
@MistyD 不,你需要使用-change - trojanfoe
我仍然困惑何时使用-change?您有可能能够解释这个的文章吗? - MistyD
@MistyD 不好意思,我没有。 - trojanfoe
@MistyD 有点晚了,但我决定重新回答。 - trojanfoe

5

install_name_tool -id 用于更改 dylib安装名称,您可以使用 otool -D 在终端中查看 dylib 的 安装名称,它会为您显示默认值,/some/path/another_library.dylibanother_library.dylib 的默认 安装名称,当然,您可以在终端中使用 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

这是我在图片中的示例。


1

id在链接时使用,install name在运行时使用。它们都是为了让链接器能够定位动态库所提供的信息。我遵循了这个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。让我们与 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.dylibid。让我们将 id 更改为 foo/liba.dylib 并重新链接。

$ 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。让我们再次链接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。

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