【问题标题】:Print rpath of an executable on macOS在 macOS 上打印可执行文件的 rpath
【发布时间】:2012-09-13 08:39:58
【问题描述】:

我想使用install_name_tool 更改可执行文件的rpath,但我现在不知道rpath 是什么。 install_name_tool 要求在命令行中同时提供旧的和新的 rpath。在 macOS 下我可以使用什么命令来打印可执行文件的 rpath

【问题讨论】:

    标签: macos command-line terminal dynamic-linking darwin


    【解决方案1】:

    首先,了解可执行文件不包含单个 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

    【讨论】:

    • @loader_path 是什么?
    • @nn0p 我发现this article 有助于理解 loader_path。
    • 我在 otool -l 输出中看不到 LC_RPATH,但我确实在 LC_ID_DYLIB cmd 条目后面的名称字段下看到了安装名称
    • 请注意,CMake 决定 forever 之前(从 3.0 开始!)默认切换到使用 @rpath,因为它们 consider it“更灵活和@executable_path@loader_path 的强大替代品。基本逻辑是,所有库都被链接并标识为@rpath/libname.dylib——没有具体路径。除了极少数例外,只要您的可执行文件的安装位置有LC_RPATH 条目,这就足够了。您不必摆弄安装后的库更新。
    【解决方案2】:

    您可以使用otool -l myexecutable,但如果您只对rpaths 列表感兴趣,这会打印出很多不必要的信息。

    您可以通过

    过滤otool -l的输出到相关的rpath条目
    otool -l myexecutable | grep RPATH -A2
    

    【讨论】:

      【解决方案3】:

      我发现我可以使用 macOS 打印共享库的安装名称

      otool -D mylib
      

      此外,我可以通过将-id 标志传递给install_name_tool 来直接设置安装名称,而无需参考旧的安装名称:

      install_name_tool -id @rpath/my/path mylib
      

      【讨论】:

        【解决方案4】:

        我目前正在编写几个用于处理 DYLD 的脚本,这个回答了这个问题,所以我将其发布以供参考:

        #!/usr/bin/env ruby
        require 'open3'
        stdout, stderr, status = Open3.capture3('/usr/bin/otool', '-l', *ARGV)
        stdout.scan(/^ +cmd LC_RPATH$.*?^ +path (.*?) \(offset \d+\)$/m) {|(p)| puts p}
        

        示例:

        lsrpath /usr/local/microsoft/powershell/7/{pwsh,createdump,unknown.xyz}
        @loader_path
        

        '希望它有所帮助;-)

        【讨论】:

        • 不是特定于 Bash-3 的技巧,但我建议在每个脚本的顶部写一个注释来解释它的作用。必须阅读代码才能找出工具存在的原因并不理想。
        【解决方案5】:

        我只是用otool命令

        otool -l <my executable>
        

        它打印出 rpath 字段。不需要任何长脚本。

        【讨论】:

        • 我也不喜欢 SO 中的长脚本,但是当您必须以编程方式修改软件包中所有二进制文件的 RPATH 时,您需要编写一些。
        猜你喜欢
        • 2018-09-26
        • 1970-01-01
        • 2012-04-03
        • 1970-01-01
        • 2015-03-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-06-22
        相关资源
        最近更新 更多