Print rpath of an executable on macOS
Asked Answered
C

5

95

I want to change the rpath of an executable using install_name_tool, but I can't figure out what the rpath is right now. install_name_tool requires both the old and the new rpath's to be given on the commandline. What command can I use to print the rpath of an executable under macOS?

Caressa answered 20/9, 2012 at 22:26 Comment(0)
W
132

First of all, understand that an executable doesn't contain a single rpath entry, but an array of one or more entries.

Second, you can use otool to list an image's rpath entries. Using otool -l, you'll get output like the following, at the very end of which are the rpath entries:

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)

Look for the LC_RPATH commands and note the path under the path entry.

EDIT: regarding what @loader_path is: it's a generic and dynamic way to refer to the Mach-O object that wants to do the loading of the framework.

While this is a rather contrived example, I think it should get the point across. Let's say we have an app MyApp.app that uses a framework MyFramework.framework. We'll also say that to function properly, I require that my app is installed in the /Applications and nowhere else. So the structure of said app and framework would be the following:

/Applications/MyApp.app/Contents/MacOS/MyApp (executable) /Applications/MyApp.app/Contents/Frameworks/MyFramework.framework/MyFramework (Mach-O dylib)

If we were to run otool -L (note the capital L) on the executable it would show the following regarding 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
....

Note that because the MyFramework.framework uses an @rpath install name/path, we'll need to have runtime search path entries that will be substituted in place of @rpath at runtime. Now, I could have a single rpath entry of:

/Applications/MyApp.app/Contents/Frameworks

That would work, and at runtime the two parts would be put together:

/Applications/MyApp.app/Contents/Frameworks + /MyFramework.framework/Versions/A/MyFramework ==

/Applications/MyApp.app/Contents/Frameworks/MyFramework.framework/Versions/A/MyFramework

Obviously, hard-coding a path like this is not ideal, as simply moving the app to a different folder or renaming the app itself would cause linking to fail.

@loader_path is simply a dynamic way to refer to the app's executable wherever it may exist on the filesystem. In this particular case, at runtime it will be filled in with the path to the running executable: /Applications/MyApp.app/Contents/MacOS/MyApp. Then we can say that to find the MyFramework.framework, you simply go up a directory and over to Frameworks.

Wolfish answered 20/9, 2012 at 22:56 Comment(4)
What's the @loader_path?Gardie
@Gardie I found this article helpful in understanding loader_path.Sack
I couldn't see an LC_RPATH in my otool -l output, but I do see the install name under the name field that follows the LC_ID_DYLIB cmd entrySpillage
Note that CMake decided forever ago at this point (starting with 3.0!) to switch over to using @rpath by default, as they consider it "a more flexible and powerful alternative to @executable_path and @loader_path". The basic logic is, all libs get linked and identified as @rpath/libname.dylib — no concrete paths. With rare exceptions, that's good enough as long as your executable has LC_RPATH entries for their installed location(s). You don't have to fiddle with post-install library updates.Leucocratic
B
19

You can use otool -l myexecutable, but this prints a lot of unnecessary information if you are interested only in the list of rpaths.

You can filter the output of otool -l to the relevant rpath entries by

otool -l myexecutable | grep RPATH -A2
Byng answered 31/10, 2018 at 13:5 Comment(0)
S
7

I found that I can print the install name of a shared library on macOS using

otool -D mylib

Moreover, I can set the install name directly without reference to the old install name by passing the -id flag to install_name_tool:

install_name_tool -id @rpath/my/path mylib
Spillage answered 28/6, 2017 at 7:46 Comment(0)
M
4

You can display the RPATHs defined in a binary file with otool -l, but the output isn't very "human-readable":

# 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)
...

So, here's a shell function that parses otool -l output and prints the RPATHes found, one per line, using awk:

lsrpath() {
    otool -l "$@" |
    awk '
        /^[^ ]/ {f = 0}
        $2 == "LC_RPATH" && $1 == "cmd" {f = 1}
        f && gsub(/^ *path | \(offset [0-9]+\)$/, "") == 2
    '
}

remark: given otool -l output format, there's no way to prevent a broken multi-line RPATH from fooling the code. I added a little safeguard with the 2 == gsub(...) which should filter out most of them.


Example:

lsrpath \
    /System/Library/CoreServices/UAUPlugins/InternationalAccountUpdater.bundle/Contents/MacOS/InternationalAccountUpdater \
    /System/Library/CoreServices/UAUPlugins/DockUpdater.bundle/Contents/MacOS/DockUpdater

Output:

/usr/lib/swift
@executable_path/../Frameworks
@loader_path/../Frameworks
/System/Library/PrivateFrameworks/Swift
/usr/lib/swift
Mugger answered 17/4, 2015 at 23:41 Comment(1)
Not a Bash-3 specific trick, but I recommend writing a comment at the top of every script that explains what it does. It's not ideal to have to read the code to figure out why a tool exists.Sagittal
A
0

I just use otool command

otool -l <my executable>

It prints out the rpath field. No need for any long scripts.

Arenaceous answered 26/2, 2016 at 17:41 Comment(1)
I also don't like long scripts in SO but when you have to modify programmatically the RPATH of all the binaries in a software package then you'll need to write some.Mugger

© 2022 - 2024 — McMap. All rights reserved.