rea-reverse

Trace macOS Dylib Paths with REA

Written for REA 6.8.0Published by rea-reverseUpdated 2 min read
On this page

A macOS binary can name a library as @rpath/libanswer.dylib, rather than as one fixed path. To understand a failed load, you need both that name and the search paths of the loading process. otool -L shows the names; REA’s trace-dylib-resolution builds a static graph and reports which candidates it could evaluate inside the selected target.

We tested REA 6.8.0 with Node 26.12.1 on Apple silicon using a tiny program built for this article. The examples inspect files; they do not launch the program. Results for your own app can differ with its architecture, bundle layout and environment.

REA 6.8.0 requires Node 22.23.4 or later within the 22.x series, Node 24.22.1 or later within 24.x, or Node 26.12.1 or later. Check node --version before installing it.

Trace macOS dylib resolution with REASilent walkthrough with English captions.

1. Build a small example

In a new working directory, create a library and an executable that names it through @rpath:

mkdir -p sample/lib missing
printf 'int answer(void) { return 42; }\n' > sample/lib/answer.c
printf 'extern int answer(void); int main(void) { return answer() == 42 ? 0 : 1; }\n' > sample/main.c
clang -dynamiclib sample/lib/answer.c \
  -Wl,-install_name,@rpath/libanswer.dylib -o sample/lib/libanswer.dylib
clang sample/main.c -Lsample/lib -lanswer \
  -Wl,-rpath,@executable_path/lib -o sample/demo
cp sample/demo missing/demo

The two executables are the same bytes. sample has the library under lib/; missing deliberately has no library. We created both directories for this test and did not remove anything from an existing app.

2. Read the load commands

otool -L sample/demo
otool -l sample/demo

Our otool -L output named @rpath/libanswer.dylib and /usr/lib/libSystem.B.dylib. In the longer otool -l output, LC_RPATH contained @executable_path/lib. That gives a candidate path for the first dependency, relative to the executable. It is not itself evidence that the candidate file is present.

3. Trace the complete copy

rea trace-dylib-resolution sample/demo --format json

For the arm64 root, the result reported an edge from demo to @rpath/libanswer.dylib. Its candidate was lib/libanswer.dylib, sourced from the root’s @executable_path/lib rpath, with outcome resolved and install_name_matches: true. The graph also included an edge to /usr/lib/libSystem.B.dylib with candidate outcome outside-target and resolution undetermined.

That second result does not mean the system library is missing. Absolute paths outside the analyzed root are not evaluated by this profile, including system libraries commonly supplied by the dyld shared cache. coverage: complete describes the graph’s analysis within its stated scope; it does not convert an undetermined system dependency into a successful load.

4. Compare the copy without the library

rea trace-dylib-resolution missing/demo --format json

The executable’s SHA-256 was unchanged in our two calls. In the second directory, the candidate lib/libanswer.dylib had outcome absent, the dependency’s resolution was unresolved, and REA reported required-load-unresolved. This is a finding about the supplied root: the required library was absent there. It is not a record of an application launch or a guarantee that nothing outside that root could affect a real load.

How to use the result

Follow an edge from its loader through its install_name, rpath owner, candidate and outcome. @executable_path refers to the process executable; @loader_path is interpreted for the image that declares the dependency. For app bundles or multi-architecture files, check which root and architecture a result describes; --architecture can select a slice.

A resolved path is still not a successful launch. REA does not evaluate code-signing/library validation, all environment-dependent fallback paths, or dylib compatibility versions. Lazy dependencies are resolved but not traversed. If a real app fails, use the static graph to choose the next check, then inspect its signing, packaging and runtime environment separately.

Next steps