[macOS翻译]rpath, otool, 和 install_name_tool 的乐趣。

3,189 阅读3分钟

原文地址:medium.com/@donblas/fu…

原文作者:medium.com/@donblas

发布时间:2018年4月27日-3分钟阅读

最近的一些工作迫使我对macOS上的原生库解析变得更加友好。我一直想把我学到的东西写下来,Xamarin.Mac论坛上的一个问题鼓励我抽出时间来。

在我开始之前,有两点要注意。

  • 理论上,Xamarin.Mac的打包工具应该可以帮你处理这个问题。如果你遇到了明显应该工作而需要手动调整的情况,请用一个示例项目提交一个问题
  • 我远不是这方面的专家,这只是一个高层次的概述。你可以在这里阅读更多细节,这是一个可靠的参考页面。我无法在任何地方找到一个漂亮的总体概述,这就是为什么我写了这个。欢迎在评论中留下更正。

当macOS加载你的可执行文件时,它也会加载任何直接依赖的动态库(和框架)。大多数情况下,这种情况不会发生(在你的代码执行之前),但是当它失败的时候,你会得到这样一条可怕的消息。

dyld: Library not loaded: MyLibrary
  Referenced from: /path/to/my/application.app/Contents/MacOS/application
  Reason: image not found

让我们来研究一下是怎么回事。不像Windows,它会在一些位置寻找,比如你的exe旁边,macOS不会(默认情况下)。otool -l会以一种人类可读的格式转储它们。

输出将列出大量的加载命令,今天我们主要关注LC_LOAD_DYLIB和LC_RPATH。

  • LC_LOAD_DYLIB表示 "请在此路径加载一个本地库"。它可以是绝对路径(/usr/lib/libSystem.B.dylib)、相对路径(.../libFoo.dylib)或特殊路径(@rpath/Foo.framework/Foo)。
  • LC_RPATH在rpath中添加了一些项目,我们稍后会介绍。

绝对路径是显而易见的,而且只有在系统库中才真正常见。相对路径往往是错误的,因为它们是基于你当前的目录(这意味着如果你从其他位置运行它可能无法工作)。

通常情况下,你会希望使用一个相对于可执行文件的位置,这就是一些特殊的"@"路径的作用。有很多不同的选项。

@executable_path, @loader_path, @rpath

和这个是一个合理的参考。

rpath对于我们的目的来说是最重要的,因为它说 "在每一个用LC_RPATH命令指定的文件夹中寻找库"。例如,这就允许主可执行文件设置一个rpath,而依赖的库可以消耗它,而不知道它一定在哪里。

现在你已经对库加载的话有了大致的了解,那么如何影响它呢?有两种常见的方法。

  • 把链接器命令传给clang(通常是-Xlinker option -Xlinker value这种形式)。
  • 使用 install_name_tool 来修改构建后的东西。

install_name_tool 有很多选项,但我常用的两个是。

install_name_tool -add_rpath @executable_path/. a.out

听起来,它将任何路径(在这种情况下,二进制旁边)添加到我的a.out

install_name_tool -change libFoo @rpath/libFoo a.out。

还有这个,它将libFoo的加载器指令从旧的值(libFoo)改为新的值(@rpath/libFoo)

还有一些环境变量,你可以设置有本机加载器输出信息。有时会甩出从什么位置解析出什么库的列表。

DYLD_PRINT_LIBRARIES=1 ./Foo.app/Contents/MacOS/Foo

可以给人以启迪。


通过www.DeepL.com/Translator(免费版)翻译