In short: dynamic linking happened. Dynamic linking - what it is? It's an operation that happens when part of the code spreads across different files (called libraries), and the binary content of the library is loaded in runtime.
A dynamic linker (which is a system tool) finds a symbol (e.g., function), located in the dynamic library. Next, loads the code into memory and assign memory address with the symbol. This way running program can find the implementation of the symbol stored in an external library (shared library). Action happens at the beginning of the program execution process before the application code starts executing (may happen later if symbols are lazily loaded)
It means that while we develop an application, we can miss it until the program runs.
If linking is not properly set up for the executable, this is the wake-up call we'll notice at the very end of the development process:
dyld: Library not loaded: @rpath/Allthethings.framework/Allthethings Referenced from: /private/var/mobile/Containers/Bundle/Application/0F2C2461-A68B-4ABA-A604-B88E6E9D1BB1/App.app/App Reason: image not found
What is says:
- while executing an App binary from App.app bundle
dyld- the dynamic linker tool from macOS system
- can't load Allthethings binary from the Allthethings.framework bundle
- due to image not found when a search for @rpath/Allthethings.framework/Allthethings binary
The short answer is: because the file is not there.
Where exactly is there? the path is @rpath/Allthethings.framework/Allthethings. This value was set while building the application. It was passed to the linker (not the compiler).
@rpath stands for Runpath Search Path.
- In the Xcode, it's set with
ldcommand tool it's set with
-rpathparameter when linking.
So it's a search path for the linker. Runtime Search Path instructs the dynamic linker to search a list of paths in order, to locate the dynamic library.
The value of the parameter may be an absolute path (or multiple paths) to a directory, e.g.:
For the relative path, we can use one of two substitution symbols:
- @loader_path - resolves with the path to the directory containing the Mach-O binary which contains the load command. Thus, in every binary, it's resolved to a different path, that said it's the path to the library doing the loading of a given library.
It's not necessary the same library. Imagine that library
libc.dylibloads another library
libqq.dylib. In this case, loader_path will point to where
libc.dylibis located (because this is caller is located).
- @executable_path - resolves to the absolute path of the executable, eg.
dyld uses the runpath when searching for dynamic libraries whose load path begins with @rpath.
How the path @rpath/Allthethings.framework/Allthething from the error message is resolved then? We don't know yet. To solve that, we need to learn about possible
rpath for the binary. In this example the binary to investigate is locate at
Runpath Search Path is stored as a part of the binary, as an
LC_RPATH command. To read the value of the section, we can use the command line tool
otool -l 0F2C2461-A68B-4ABA-A604-B88E6E9D1BB1/App.app/App and search for
LC_RPATH in the output:
Load command 48 cmd LC_RPATH cmdsize 48 path @executable_path/../Frameworks (offset 12)
there are one or more entries like this.
Now we can substitute @rpath with the found path and verify why the file is missing.
To fix the initial problem we'll need to either:
- copy binary to the expected directory
- add another Runpath Search Path by modifying the value of
I've said the @rpath is part of the binary and is set during the compilation process (linking phase) with a given value of the parameters. However, if we need to modify the @rpath manually, e.g., as a part of installation phase - there's an app for that:
install_name_tool changes dynamic shared library install names and manipulate Runpaths.
To add new path:
install_name_tool -add_rpath @executable_path/../private/libs File
To delete added path (we can only delete path added with
install_name_tool -delete_rpath @executable_path/../private/libs File
To change the existing path:
install_name_tool -rpath @executable_path/../Frameworks @executable_path/../private/libs File
We just mastered dynamic linking problem-solving. The meaning of the Embedded Binaries section from the Xcode is clear:
Xcode copies files from "Embedded Binaries" section to the place where
LD_RUNPATH_SEARCH_PATH points to.
Controling linker with environment variables
The behaviour of
dyld may be controlled by environment variables, however If System Integrity Protection is enabled, environment variables are ignored when executing binaries protected by System Integrity Protection - that is most of the time.
PS. it's different on Linux because different loader is used.