Hi all,
while debugging a fortran runtime library on a recent MacOS, I tried to use the
libsanitizer provided with gcc. It compiled, but no asan enabled program could
start on MacOS Sequoia. I figured from libsanitizers github, that the shared
cache needs to be consulted from some OS version on. The support present in
github uses proprietary extensions to C++, that do not compile on gcc. I figured
how to translate this into pure C++, which what the patch is about. The
solution is somewhat hacky and I don't know what to do with it. It works,
address faults are reported correctly at least on
x86_64-apple-darwin24.something. I am open for suggestions what to do it.
I do not assume, that upstream libsanitizer will apply the patch, because it is
GCC specific.
I tried to rebase GCC's libsanitizer to the github's state, but there are more
locations where now proprietary extensions are used. To not loose the patch, I
thought I publish it here, so that at least some one may find it.
Regards,
Andre
--
Andre Vehreschild * Email: vehre ad gmx dot de
From b6a79171c253a5046e800a491d98b6169518d135 Mon Sep 17 00:00:00 2001
From: Andre Vehreschild <[email protected]>
Date: Mon, 22 Sep 2025 14:03:42 +0200
Subject: [PATCH] Libsanitizer: Mimick Objective-C for shared cache travesal on
macOS.
Setup an Objective-C "functor" style object from C++ to traverse the
MacOS shared cache. Most of the code comes from the original repo. The
original code unfortunately only compile on clang due to certain
extensions.
libsanitizer/ChangeLog:
* sanitizer_common/sanitizer_procmaps_mac.cpp (struct dyld_shared_cache_dylib_text_info):
Define MacOS internal structure for shared cache traversal.
(_dyld_get_shared_cache_uuid): Define MacOS internal routine.
(_dyld_get_shared_cache_range): Same.
(dyld_shared_cache_iterate_text): Same.
(class HeaderIterator): Define a functor to mimick an
Objective-C style object from C++.
(GetDyldImageHeaderViaSharedCache): Traverse the shared cache.
(get_dyld_hdr): Traverse the shared cache where available.
---
.../sanitizer_procmaps_mac.cpp | 75 +++++++++++++++++--
1 file changed, 68 insertions(+), 7 deletions(-)
diff --git a/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp b/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp
index 64e9c4858b6..246c463d676 100644
--- a/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp
+++ b/libsanitizer/sanitizer_common/sanitizer_procmaps_mac.cpp
@@ -176,19 +176,80 @@ static mach_header *GetDyldImageHeaderViaVMRegion() {
}
}
+extern "C" {
+struct dyld_shared_cache_dylib_text_info {
+ uint64_t version; // current version 2
+ // following fields all exist in version 1
+ uint64_t loadAddressUnslid;
+ uint64_t textSegmentSize;
+ uuid_t dylibUuid;
+ const char *path; // pointer invalid at end of iterations
+ // following fields all exist in version 2
+ uint64_t textSegmentOffset; // offset from start of cache
+};
+typedef struct dyld_shared_cache_dylib_text_info dyld_shared_cache_dylib_text_info;
+extern bool _dyld_get_shared_cache_uuid(uuid_t uuid);
+extern const void *_dyld_get_shared_cache_range(size_t *length);
+extern int dyld_shared_cache_iterate_text(const uuid_t cacheUuid, const void *);
+} // extern "C"
+
+class HeaderIterator {
+public:
+ uptr cacheStart = 0;
+ mach_header *dyld_hdr;
+ typedef void(callback_t)(HeaderIterator *, const dyld_shared_cache_dylib_text_info *);
+ callback_t *callback;
+
+ static void cb_shim(HeaderIterator *that, const dyld_shared_cache_dylib_text_info *info) {
+ if (that)
+ that->find_header (info);
+ }
+
+ void find_header (const dyld_shared_cache_dylib_text_info *info) {
+ CHECK_GE(info->version, 2);
+ mach_header *hdr = (mach_header *) (cacheStart + info->textSegmentOffset);
+ if (IsDyldHdr(hdr))
+ dyld_hdr = hdr;
+ }
+
+ HeaderIterator(uptr cacheStart_)
+ : cacheStart(cacheStart_)
+ , dyld_hdr(nullptr) {
+ callback = &HeaderIterator::cb_shim;
+ }
+};
+
+static mach_header *GetDyldImageHeaderViaSharedCache() {
+ uuid_t uuid;
+ bool hasCache = _dyld_get_shared_cache_uuid(uuid);
+ if (!hasCache)
+ return nullptr;
+
+ size_t cacheLength;
+ uptr cacheStart = (uptr) _dyld_get_shared_cache_range(&cacheLength);
+ CHECK(cacheStart && cacheLength);
+
+ HeaderIterator hi(cacheStart);
+ int res = dyld_shared_cache_iterate_text(uuid, &hi);
+ CHECK_EQ(res, 0);
+
+ return hi.dyld_hdr;
+}
+
const mach_header *get_dyld_hdr() {
if (!dyld_hdr) {
// On macOS 13+, dyld itself has moved into the shared cache. Looking it up
// via vm_region_recurse_64() causes spins/hangs/crashes.
- // FIXME: find a way to do this compatible with GCC.
if (GetMacosAlignedVersion() >= MacosVersion(13, 0)) {
- VReport(1,
- "looking up the dyld image header in the shared cache on "
- "macOS 13+ is not yet supported. Falling back to "
- "lookup via vm_region_recurse_64().\n");
- dyld_hdr = GetDyldImageHeaderViaVMRegion();
+ dyld_hdr = GetDyldImageHeaderViaSharedCache();
+ if (!dyld_hdr) {
+ Printf("Failed to lookup the dyld image header in the shared cache on "
+ "macOS 13+ (or no shared cache in use). Falling back to lookup via"
+ "vm_region_recurse_64().\n");
+ dyld_hdr = GetDyldImageHeaderViaVMRegion();
+ }
} else {
- dyld_hdr = GetDyldImageHeaderViaVMRegion();
+ dyld_hdr = GetDyldImageHeaderViaVMRegion();
}
CHECK(dyld_hdr);
}
--
2.51.0