我想使用 install_name_tool
更改可执行文件的
rpath,但我现在无法弄清楚 rpath 是什么。
install_name_tool
要求在命令行上给出旧的和新的 rpath。我可以使用什么命令在 macOS 下打印可执行文件的rpath?
首先,请了解可执行文件不包含单个
rpath
条目,而是包含一个或多个条目的数组。
其次,您可以使用
otool
列出图像的 rpath
条目。使用 otool -l
,您将得到如下输出,其末尾是 rpath
条目:
Load command 34
cmd LC_LOAD_DYLIB
cmdsize 88
name /System/Library/Frameworks/AppKit.framework/Versions/C/AppKit (offset 24)
time stamp 2 Wed Dec 31 19:00:02 1969
current version 1038.32.0
compatibility version 45.0.0
Load command 35
cmd LC_RPATH
cmdsize 40
path @loader_path/../Frameworks (offset 12)
查找
LC_RPATH
命令并记下 path
条目下的路径。
编辑:关于
@loader_path
是什么:它是一种通用且动态的方式来引用想要加载框架的Mach-O对象。
虽然这是一个相当做作的例子,但我认为它应该能够表达要点。假设我们有一个应用程序
MyApp.app
,它使用框架 MyFramework.framework
。我们还会说,为了正常运行,我要求我的应用程序安装在 /Applications 中,而不是其他地方。因此,所述应用程序和框架的结构如下:
/Applications/MyApp.app/Contents/MacOS/MyApp
(可执行)
/Applications/MyApp.app/Contents/Frameworks/MyFramework.framework/MyFramework
(Mach-O dylib)
如果我们要在可执行文件上运行
otool -L
(注意大写 L),它将显示有关 MyFramework 的以下内容:
@rpath/MyFramework.framework/Versions/A/MyFramework
/System/Library/Frameworks/Cocoa.framework/Versions/A/Cocoa
/System/Library/Frameworks/Foundation.framework/Versions/C/Foundation
/usr/lib/libobjc.A.dylib
/usr/lib/libSystem.B.dylib
....
请注意,由于 MyFramework.framework 使用
@rpath
安装名称/路径,因此我们需要有运行时搜索路径条目,这些条目将在运行时替换 @rpath
。现在,我可以有一个 rpath 条目:
/Applications/MyApp.app/Contents/Frameworks
这可行,并且在运行时这两个部分将放在一起:
/Applications/MyApp.app/Contents/Frameworks
+ /MyFramework.framework/Versions/A/MyFramework
==
/Applications/MyApp.app/Contents/Frameworks/MyFramework.framework/Versions/A/MyFramework
显然,硬编码这样的路径并不理想,因为简单地将应用程序移动到不同的文件夹或重命名应用程序本身会导致链接失败。
@loader_path
只是一种动态方式来引用应用程序的可执行文件,无论它存在于文件系统上的任何位置。在这种特殊情况下,在运行时它将填充正在运行的可执行文件的路径:/Applications/MyApp.app/Contents/MacOS/MyApp
。然后我们可以说,要找到 MyFramework.framework,您只需转到目录并转到 Frameworks
。
您可以使用
otool -l myexecutable
,但是如果您只对 rpath 列表感兴趣,这会打印很多不必要的信息。
您可以通过
将
otool -l
的输出过滤到相关 rpath 条目
otool -l myexecutable | grep RPATH -A2
我发现我可以使用
在 macOS 上打印共享库的安装名称otool -D mylib
此外,我可以通过将
-id
标志传递给 install_name_tool
来直接设置安装名称,而不参考旧的安装名称:
install_name_tool -id @rpath/my/path mylib
您可以使用
otool -l
显示二进制文件中定义的 RPATH;但输出格式的可读性不太好:
# otool -l /usr/bin/php
...
Load command 32
cmd LC_RPATH
cmdsize 136
path /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.14.Internal.sdk/usr/lib (offset 12)
...
因此,这是一个 shell 函数,它解析
otool -l
并使用 awk
: 输出找到的 RPATH(每行一个)
lsrpath() {
otool -l "$@" |
awk '
/^[^ ]/ {f = 0}
$2 == "LC_RPATH" && $1 == "cmd" {f = 1}
f && gsub(/^ *path | \(offset [0-9]+\)$/, "") == 2
'
}
备注:给定
otool -l
输出格式,无法防止损坏的多行 RPATH 欺骗代码。我用 2 == gsub(...)
添加了一些防护措施,应该可以过滤掉大部分内容。
示例:
lsrpath \
/System/Library/CoreServices/UAUPlugins/InternationalAccountUpdater.bundle/Contents/MacOS/InternationalAccountUpdater \
/System/Library/CoreServices/UAUPlugins/DockUpdater.bundle/Contents/MacOS/DockUpdater
输出:
/usr/lib/swift
@executable_path/../Frameworks
@loader_path/../Frameworks
/System/Library/PrivateFrameworks/Swift
/usr/lib/swift
我只使用otool命令
otool -l <my executable>
它打印出 rpath 字段。不需要任何长脚本。