Patching an Macho file
Practical CLI Tutorial: Patching a Mach-O Binary#
This tutorial demonstrates a common use case for arwen: patching a Mach-O executable so it can find its required shared library (.dylib file) using a relative path. This makes the application "relocatable," meaning you can install it in different locations without breaking its ability to find its dependencies. This is typically achieved using @rpath.
Goal:
To modify an application (my_app) that depends on a library (libcustom.dylib) so that my_app can find libcustom.dylib when they are placed in a specific directory structure (e.g., bin/ and lib/ subdirectories within a main installation folder).
Scenario:
- We have a simple Mach-O executable named
my_app. my_appdepends on a custom Mach-O shared library namedlibcustom.dylib.- Initially,
my_appis compiled to look forlibcustom.dylibin a path specified by@rpath, but the application itself does not yet have anrpathconfigured. - We want to install the application into
/opt/my_app/, placing the executable at/opt/my_app/bin/my_appand the library at/opt/my_app/lib/libcustom.dylib. - We will use
arwento patch/opt/my_app/bin/my_appto add anrpaththat tells it to look forlibcustom.dylibin the adjacent../libdirectory.
Prerequisites:
- The
arwencommand-line tool installed and in your PATH. - A simple Mach-O executable (
my_app) and a Mach-O shared library (libcustom.dylib) it depends on.- Note: You can create these with a C compiler like
clang(standard on macOS):libcustom.c:int custom_function() { return 42; }my_app.c:int custom_function(); int main() { return custom_function(); }- Compile:
clang -dynamiclib -o libcustom.dylib libcustom.c -install_name "@rpath/libcustom.dylib"clang my_app.c -o my_app -L. -lcustom(Link against the library in the current dir)
- Note: You can create these with a C compiler like
Step 1: Inspecting the Initial State
First, let's see how my_app currently finds its dependency and what its embedded paths look like. Assume my_app and libcustom.dylib are in your current directory.
You'll see output similar to this. Note the @rpath/libcustom.dylib entry. This tells the loader to search for the library in the runtime search paths (rpaths).
./my_app:
@rpath/libcustom.dylib (compatibility version 0.0.0, current version 0.0.0)
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1319.100.3)
You will see no output, as we have not set any rpath yet. This means the application won't find libcustom.dylib if you try to run it.
Step 2: Patching the Mach-O Binary
Now, we will patch my_app to add a relative rpath that points to where libcustom.dylib will be located. We use @executable_path which is a special variable that resolves to the directory containing the binary.
If we run arwen macho print-rpaths my_app again, we should see the new rpath:
my_app in a bin/ directory and libcustom.dylib in a sibling lib/ directory, the executable would successfully find its library at runtime.
Step 3: Removing a Dependency Sometimes you might want to remove a dependency entry from the binary, for example when stripping dependencies.
Now, if we check the dependencies again with otool, we should see that libcustom.dylib is no longer listed as a required dependency.
Conclusion:
In this small tutorial, we took a look in a practical example how to use arwen for patching Mach-O binaries and setup a different rpath. This allows the application to find its dependencies in a custom directory structure using @executable_path, making it more portable and easier to deploy on macOS.