原文作者: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(免费版)翻译