When I was building my C++ iOS game project in Xcode, I decided to enable Address Sanitizer (ASan) and Undefined Behavior Sanitizer (UBSan) to catch subtle memory issues and undefined behaviors during development. What I didn’t expect was to run headfirst into a confusing and frustrating linker error one that stalled my build completely.
The Linker Error That Stopped
Here’s the exact error message I saw in Xcode:
Ditto /Users/Max/Library/Developer/Xcode/DerivedData/TowerDuel-dsejjkbvgengpngaqrfokeoaquvx/Build/Products/Debug-iphoneos/TowerDuel-mobile.app/Frameworks/libclang_rt.asan_ios_dynamic.dylib /Users/Max/Documents/XcodeWorkspace/TowerDuelWorkspace/TowerDuel/lib/clang/8.1.0/lib/darwin/libclang_rt.asan_ios_dynamic.dylib
cd /Users/Max/Documents/XcodeWorkspace/TowerDuelWorkspace/TowerDuel/proj.ios_mac
export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/bin:/bin:/usr/sbin:/sbin"
/usr/bin/ditto -rsrc /Users/Max/Documents/XcodeWorkspace/TowerDuelWorkspace/TowerDuel/lib/clang/8.1.0/lib/darwin/libclang_rt.asan_ios_dynamic.dylib /Users/Max/Library/Developer/Xcode/DerivedData/TowerDuel-dsejjkbvgengpngaqrfokeoaquvx/Build/Products/Debug-iphoneos/TowerDuel-mobile.app/Frameworks/libclang_rt.asan_ios_dynamic.dylib
ditto: can't get real path for source '/Users/Max/Documents/XcodeWorkspace/TowerDuelWorkspace/TowerDuel/lib/clang/8.1.0/lib/darwin/libclang_rt.asan_ios_dynamic.dylib'
Command /usr/bin/ditto failed with exit code 1
Explain Error
After investigating, I realized Xcode was trying to manually copy a sanitizer runtime library (libclang_rt.asan_ios_dynamic.dylib
) from an old, hardcoded path specifically from a Clang 8.1.0 directory that no longer existed on my system.
This was problematic for a few key reasons:
- My deployment target was iOS 9, but the build script was looking for iOS 8.1 libraries.
- That exact path (
/lib/clang/8.1.0/lib/darwin/...
) simply didn’t exist anymore the build script failed because it couldn’t find the file. - Sanitizer libraries are supposed to be automatically included by Xcode, not manually linked or copied in modern setups.
How I Fix The Issue
Here’s the step-by-step solution that worked for me:
Removed the Hardcoded Library Reference
Somewhere in my project or build script, the path to libclang_rt.asan_ios_dynamic.dylib
was hardcoded. I removed any custom scripts or manual copying instructions like this:
/lib/clang/8.1.0/lib/darwin/libclang_rt.asan_ios_dynamic.dylib
These are not needed anymore when using newer Xcode versions.
Update Xcode and Clean Derive Data
I was originally using an older version of Xcode. Upgrading to Xcode 11+ fixed a lot of subtle sanitizer integration issues. I also cleaned out any old project artifacts:
rm -rf ~/Library/Developer/Xcode/DerivedData
In Xcode, I also went to Product > Clean Build Folder
.
Enable Sanitizer via Scheme
Instead of manually tweaking build settings or runtime libraries, I let Xcode manage everything:
- Go to Product > Scheme > Edit Scheme
- Under the Diagnostics tab:
- Check “Address Sanitizer”
- Check “Undefined Behavior Sanitizer”
This ensures Xcode includes the required runtime libraries from the current SDK.
Bonus Improvement
Once the sanitizers were working again, I added more practices to improve stability and catch bugs faster during development.
Custom Assertion Handler in Debug Build
This lets me catch failed assertions with meaningful logging:
#ifdef DEBUG
#include <cassert>
#include <iostream>
void EnableCustomAssertions() {
std::set_terminate([](){
std::cerr << "Custom Assertion Failed. Terminating...\n";
std::abort();
});
}
#endif
I call EnableCustomAssertions();
in my game initialization logic.
Skipping Sanitizer for Known Unsafe Function
If I know a function causes false positives but is safe for my case:
__attribute__((no_sanitize("undefined")))
void UnsafeButKnownFunction() {
// Skips UBSan here
}
Strict Compiler Flag
In Build Settings > Other C++ Flags, I added:
-fsanitize=undefined,address -fno-omit-frame-pointer -O1
This gives better sanitizer feedback during runtime. I avoid -O2
or -O3
while debugging.
Sanitizer Check Script
To prevent future issues, I added a build phase script:
if [ "$ENABLE_ADDRESS_SANITIZER" = "YES" ]; then
echo "ASan is enabled"
if [ ! -f "$SDKROOT/usr/lib/clang/$(clang --version | grep version | awk '{print $3}')/lib/darwin/libclang_rt.asan_ios_dynamic.dylib" ]; then
echo "Missing ASan runtime! Please update Xcode or fix your scheme."
exit 1
fi
fi
This alerts me during the build if something is misconfigured.
Final Thought
This whole issue boiled down to an outdated, hardcoded reference to sanitizer runtime libraries. In modern Xcode setups, you should never need to manually link these libraries. Just let Xcode manage them through the scheme settings.