在macOS上打印可执行文件的rpath

在macOS上打印可执行文件的rpath,macos,command-line,terminal,dynamic-linking,darwin,Macos,Command Line,Terminal,Dynamic Linking,Darwin,我想使用install\u name\u tool更改可执行文件的rpath,但我现在无法确定rpath是什么install\u name\u工具要求在命令行中同时提供旧的和新的rpath。我可以使用什么命令在macOS下打印可执行文件的rpath?首先,要了解可执行文件不包含单个rpath条目,而是包含一个或多个条目的数组 其次,您可以使用otool列出图像的rpath条目。使用otool-l,您将获得如下输出,最后是rpath条目: Load command 34 cmd

我想使用
install\u name\u tool
更改可执行文件的rpath,但我现在无法确定rpath是什么
install\u name\u工具
要求在命令行中同时提供旧的和新的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\u 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\u path
只是一种动态方式,用于引用文件系统中可能存在的应用程序的可执行文件。在这种特殊情况下,在运行时,它将填充运行的可执行文件的路径:
/Applications/MyApp.app/Contents/MacOS/MyApp
。然后我们可以说,要找到MyFramework.framework,只需进入一个目录并转到
Frameworks
,我目前正在编写几个用于处理DYLD的Bash-3脚本,这个脚本回答了这个问题,因此我将其发布以供参考:

#! /bin/bash

# ######################################################################### #

if [ ${#} -eq 0 ]
then
    echo "
Usage: ${0##*/} FILE...

List rpaths in FILEs
"    
    exit 0
fi

# ######################################################################### #

shopt -s extglob

# ######################################################################### #

for file in "${@}"
do
    if [ ! -r "${file}" ]
    then
        echo "${file}: no such file" 1>&2
        continue
    fi

    if ! [[ "$(/usr/bin/file "${file}")" =~ ^${file}:\ *Mach-O\ .*$ ]]
    then
        echo "${file}: is not an object file" 1>&2
        continue
    fi

    if [ ${#} -gt 1 ]
     then
         echo "${file}:"
    fi

    IFS_save="${IFS}"
    IFS=$'\n'

    _next_path_is_rpath=

    while read line
    do
        case "${line}" in
            *(\ )cmd\ LC_RPATH)
                _next_path_is_rpath=yes
                ;;
            *(\ )path\ *\ \(offset\ +([0-9])\))
                if [ -z "${_next_path_is_rpath}" ]
                then
                    continue
                fi
                line="${line#* path }"
                line="${line% (offset *}"
                if [ ${#} -gt 1 ]
                then
                    line=$'\t'"${line}"
                fi
                echo "${line}"
                _next_path_is_rpath=
                ;;
        esac
    done < <(/usr/bin/otool -l "${file}")

    IFS="${IFS_save}"
done

# ######################################################################### #
#/bin/bash
# ######################################################################### #
如果[${}-eq 0]
然后
回声“
用法:${0##*/}文件。。。
列出文件中的路径
"    
出口0
fi
# ######################################################################### #
shopt-s extglob
# ######################################################################### #
对于“${@}”中的文件
做
如果[!-r“${file}”]
然后
echo“${file}:没有这样的文件”1>&2
持续
fi
如果![[“$(/usr/bin/file“${file}”)”=~^${file}:\*Mach-O\.*$]
然后
echo“${file}:不是对象文件”1>&2
持续
fi
如果[${}-gt 1]
然后
回显“${file}”:
fi
IFS_save=“${IFS}”
IFS=$'\n'
_下一个路径是路径=
读行时
做
中的大小写“${line}”
*(\)cmd\LC\u RPATH)
_下一个路径是
;;
*(\)路径\*\\(偏移量\+([0-9])\)
如果[-z“${u next_path_是_rpath}”]
然后
持续
fi
line=“${line#*path}”
line=“${line%(偏移量*}”
如果[${}-gt 1]
然后
line=$'\t'${line}”
fi
回显“${line}”
_下一个路径是路径=
;;
以撒

完成<我只使用otool命令

otool -l <my executable>
otool-l

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

我发现我可以使用

otool -D mylib
此外,我可以通过将
-id
标志传递到
install\u name\u工具
,直接设置安装名称,而无需参考旧的安装名称:

install_name_tool -id @rpath/my/path mylib

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

您可以通过以下方式将
otool-l
的输出过滤到相关的rpath条目

otool -l myexecutable | grep RPATH -A2

什么是
@loader\u path
?@nn0p我发现有助于理解loader\u path。我在我的otool-l输出中看不到LC\u RPATH,但我确实在LC\u ID\u DYLIB cmd条目后面的name字段下看到了安装名称,这不是Bash-3特有的技巧,但我建议在每个脚本的顶部写一条注释来解释它的作用。这是It’要想弄清楚工具存在的原因,阅读代码并不理想。