When deploying complex C++ or Objective-C applications on macOS, one of the most frustrating errors developers and system administrators encounter is the dreaded Library not loaded: image not found crash upon application launch. This failure occurs when the macOS dynamic linker (dyld) cannot locate or validate the shared libraries (.dylib or frameworks) required by the Mach-O executable. While legacy Unix tools like otool -L provide a basic overview of linked libraries, they are increasingly inadequate for diagnosing complex dependency chains involving Weak Links, Re-exports, and Relative Runpath (@rpath) resolutions. To deeply analyze Mach-O binaries and resolve dependency hell, macOS engineers must utilize the highly specialized dyld_info command-line utility.
Understanding Mach-O and the Dynamic Linker
Every compiled executable on macOS utilizes the Mach-O (Mach Object) file format. When you execute an application, the kernel loads the binary into memory and immediately hands control to the dynamic linker (located at /usr/lib/dyld). The dynamic linker is responsible for reading the Load Commands embedded within the Mach-O header, locating the specified shared libraries on disk, loading them into the process’s memory space, and binding the unresolved symbols (e.g., function calls).
If a library is missing, has an incompatible CPU architecture (e.g., compiled for Intel x86_64 but running on Apple Silicon ARM64 without Rosetta), or suffers from a code signing violation, dyld aborts the launch.
Basic Dependency Inspection
The dyld_info tool is bundled natively within the Xcode Command Line Tools. Its primary function is to parse and display the specific dyld load commands embedded in the binary.
To view the direct dependencies of an executable, utilize the -dependents flag:
dyld_info -dependents /Applications/MyApp.app/Contents/MacOS/MyApp
Unlike otool, dyld_info provides a strictly parsed, hierarchical output. It clearly distinguishes between:
- Regular Dependencies: The library must be present, or the app crashes immediately.
- Weak Dependencies: The library is optional. If it is missing,
dyldwill simply set all associated function pointers toNULL, allowing the application to launch and dynamically handle the missing feature at runtime. - Re-exported Dependencies: A library that acts as an umbrella, seamlessly forwarding symbols to another underlying library (commonly seen in Apple’s core frameworks).
Analyzing RPATH and @executable_path
Modern macOS applications are frequently distributed as self-contained bundles, meaning they ship their custom .dylib files alongside the main executable rather than installing them into global system directories like /usr/local/lib. To achieve this, developers utilize special linker variables: @executable_path, @loader_path, and @rpath.
If an application crashes because it cannot find an @rpath library, you must determine what the Runpath is actually resolving to. Use the -rpaths flag to extract the embedded paths:
dyld_info -rpaths /Applications/MyApp.app/Contents/MacOS/MyApp
This command will output the exact directories the dynamic linker will search relative to the executable (e.g., @executable_path/../Frameworks). If the required .dylib is not physically present in one of those resolved directories, the launch will fail.
Exported and Imported Symbols
In advanced debugging scenarios, an application might successfully load a library but still crash with a Symbol not found error. This occurs when the application was compiled against a newer version of the library (expecting a specific function), but at runtime, an older version of the library is loaded that lacks that function.
You can use dyld_info to dump the exact C/C++ symbols the executable is importing (requiring from external libraries) and exporting (providing to others):
dyld_info -imports /Applications/MyApp.app/Contents/MacOS/MyApp
dyld_info -exports /usr/local/lib/libcustom.dylib
By comparing the required imports of the application against the available exports of the target library, you can pinpoint the exact missing function or class method causing the catastrophic linkage failure.
Inspecting Universal Binaries (Fat Binaries)
With the transition to Apple Silicon, many macOS binaries are “Universal,” containing both x86_64 and arm64 architectures within a single file. By default, dyld_info inspects the architecture matching your current host system. To explicitly inspect the dependencies of the alternate architecture (e.g., debugging Rosetta 2 linkage issues on an M2 Mac), use the -arch flag:
dyld_info -arch x86_64 -dependents /Applications/MyApp.app/Contents/MacOS/MyApp
Mastering dyld_info allows macOS systems engineers to move beyond generic crash logs and perform surgical analysis of the Mach-O binary structure, ensuring highly complex, multi-architecture application deployments function flawlessly.