https://github.com/JDevlieghere updated https://github.com/llvm/llvm-project/pull/205434
>From aeda87a636ee393c3bc75399446dad738b66877d Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <[email protected]> Date: Tue, 23 Jun 2026 14:14:25 -0700 Subject: [PATCH 1/2] [lldb] Resolve dyld introspection SPIs with dlsym (NFC) HostInfoMacOSX's SharedCacheInfo used the dyld process-snapshot introspection SPIs only when <mach-o/dyld_introspection.h> was present, gating the calling code behind a compile-time macro. To avoid bifurcating the behavior based on the SDK, rather than the presence of the symbols, use dlsym to resolve them at runtime. If the symbols are missing, we continue falling back to walking lldb's own shared cache mapping exactly as before. While here, fold the duplicate dlsym of dyld_image_segment_data_ into the new, once-initialized, shared table. Assisted-by: Claude --- .../Host/macosx/objcxx/HostInfoMacOSX.mm | 137 +++++++++++------- 1 file changed, 87 insertions(+), 50 deletions(-) diff --git a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm index ad62757a37298..6166843f2e597 100644 --- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm +++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm @@ -47,13 +47,6 @@ #include <Foundation/Foundation.h> #include <Security/Security.h> #include <mach-o/dyld.h> -#if defined(MAC_OS_X_VERSION_MIN_REQUIRED) && \ - MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_VERSION_12_0 -#if __has_include(<mach-o/dyld_introspection.h>) -#include <mach-o/dyld_introspection.h> -#define SDK_HAS_NEW_DYLD_INTROSPECTION_SPIS -#endif -#endif #include <objc/objc-auto.h> // These are needed when compiling on systems @@ -687,6 +680,71 @@ void dyld_shared_cache_for_each_image(dyld_shared_cache_t cache, } namespace { +/// Function pointers for dyld SPIs that may be absent from the SDK LLDB is +/// built against but present in libdyld at runtime. Resolving them with dlsym +/// rather than linking against them keeps a single binary working across SDKs +/// and OS versions; a null pointer means the running dyld doesn't vend that +/// SPI and the caller must fall back. +struct LazyDyldSPIs { + // Process snapshot introspection SPIs, available on macOS 12 and newer. + dyld_process_t (*process_create_for_current_task)(void) = nullptr; + void (*process_dispose)(dyld_process_t process) = nullptr; + dyld_process_snapshot_t (*process_snapshot_create_for_process)( + dyld_process_t process, kern_return_t *error) = nullptr; + void (*process_snapshot_dispose)(dyld_process_snapshot_t snapshot) = nullptr; + dyld_shared_cache_t (*process_snapshot_get_shared_cache)( + dyld_process_snapshot_t snapshot) = nullptr; + + // Shared cache image segment SPIs, available on macOS 26.4 and newer. + void (*image_retain_4HWTrace)(void *image) = nullptr; + void (*image_release_4HWTrace)(void *image) = nullptr; + dispatch_data_t (*image_segment_data_4HWTrace)( + void *image, const char *segment_name) = nullptr; + + bool HasProcessSnapshotSPIs() const { + return process_create_for_current_task && process_dispose && + process_snapshot_create_for_process && process_snapshot_dispose && + process_snapshot_get_shared_cache; + } + + bool Has4HWTraceSPIs() const { + return image_retain_4HWTrace && image_release_4HWTrace && + image_segment_data_4HWTrace; + } +}; + +const LazyDyldSPIs &GetLazyDyldSPIs() { + static LazyDyldSPIs g_spis; + static std::once_flag g_once_flag; + std::call_once(g_once_flag, []() { + auto lookup = [](const char *name) { return dlsym(RTLD_DEFAULT, name); }; + g_spis.process_create_for_current_task = + (decltype(g_spis.process_create_for_current_task))lookup( + "dyld_process_create_for_current_task"); + g_spis.process_dispose = + (decltype(g_spis.process_dispose))lookup("dyld_process_dispose"); + g_spis.process_snapshot_create_for_process = + (decltype(g_spis.process_snapshot_create_for_process))lookup( + "dyld_process_snapshot_create_for_process"); + g_spis.process_snapshot_dispose = + (decltype(g_spis.process_snapshot_dispose))lookup( + "dyld_process_snapshot_dispose"); + g_spis.process_snapshot_get_shared_cache = + (decltype(g_spis.process_snapshot_get_shared_cache))lookup( + "dyld_process_snapshot_get_shared_cache"); + g_spis.image_retain_4HWTrace = + (decltype(g_spis.image_retain_4HWTrace))lookup( + "dyld_image_retain_4HWTrace"); + g_spis.image_release_4HWTrace = + (decltype(g_spis.image_release_4HWTrace))lookup( + "dyld_image_release_4HWTrace"); + g_spis.image_segment_data_4HWTrace = + (decltype(g_spis.image_segment_data_4HWTrace))lookup( + "dyld_image_segment_data_4HWTrace"); + }); + return g_spis; +} + class SharedCacheInfo { public: SharedCacheImageInfo GetByFilename(UUID sc_uuid, ConstString filename) { @@ -734,26 +792,11 @@ SharedCacheImageInfo GetByUUID(UUID sc_uuid, UUID file_uuid) { UUID m_host_uuid; llvm::sys::RWMutex m_mutex; - - // macOS 26.4 and newer - void (*m_dyld_image_retain_4HWTrace)(void *image); - void (*m_dyld_image_release_4HWTrace)(void *image); - dispatch_data_t (*m_dyld_image_segment_data_4HWTrace)( - void *image, const char *segmentName); }; } // namespace SharedCacheInfo::SharedCacheInfo(SymbolSharedCacheUse sc_mode) { - // macOS 26.4 and newer - m_dyld_image_retain_4HWTrace = - (void (*)(void *))dlsym(RTLD_DEFAULT, "dyld_image_retain_4HWTrace"); - m_dyld_image_release_4HWTrace = - (void (*)(void *))dlsym(RTLD_DEFAULT, "dyld_image_release_4HWTrace"); - m_dyld_image_segment_data_4HWTrace = - (dispatch_data_t(*)(void *image, const char *segmentName))dlsym( - RTLD_DEFAULT, "dyld_image_segment_data_4HWTrace"); - uuid_t dsc_uuid; _dyld_get_shared_cache_uuid(dsc_uuid); m_host_uuid = UUID(dsc_uuid); @@ -770,13 +813,12 @@ SharedCacheImageInfo GetByUUID(UUID sc_uuid, UUID file_uuid) { if (use_libdyld_spi && CreateHostSharedCacheImageList()) return; - // Scan lldb's shared cache memory if we're built against the - // internal SDK and have those headers. + // Otherwise scan lldb's own shared cache, preferring the process snapshot + // SPIs when the running dyld vends them. if (CreateSharedCacheInfoWithInstrospectionSPIs()) return; - // Scan lldb's shared cache memory if we're built against the public - // SDK. + // Fall back to walking the shared cache mapping directly. CreateSharedCacheInfoLLDBsVirtualMemory(); } @@ -796,15 +838,8 @@ static DataExtractorSP map_shared_cache_binary_segments(void *image) { static std::mutex g_mutex; std::lock_guard<std::mutex> guard(g_mutex); - static dispatch_data_t (*g_dyld_image_segment_data_4HWTrace)( - void *image, const char *segmentName); - static std::once_flag g_once_flag; - std::call_once(g_once_flag, [&]() { - g_dyld_image_segment_data_4HWTrace = - (dispatch_data_t(*)(void *, const char *))dlsym( - RTLD_DEFAULT, "dyld_image_segment_data_4HWTrace"); - }); - if (!g_dyld_image_segment_data_4HWTrace) + const LazyDyldSPIs &dyld = GetLazyDyldSPIs(); + if (!dyld.image_segment_data_4HWTrace) return {}; __block std::vector<segment> segments; @@ -818,7 +853,7 @@ static dispatch_data_t (*g_dyld_image_segment_data_4HWTrace)( seg.vmsize = vmSize; dispatch_data_t data_from_libdyld = - g_dyld_image_segment_data_4HWTrace(image_copy, segmentName); + dyld.image_segment_data_4HWTrace(image_copy, segmentName); (void)dispatch_data_create_map(data_from_libdyld, &seg.data, &seg.size); if (seg.size > 0 && seg.data != 0) @@ -872,8 +907,8 @@ static dispatch_data_t (*g_dyld_image_segment_data_4HWTrace)( bool SharedCacheInfo::CreateSharedCacheImageList(UUID sc_uuid, std::string filepath) { llvm::sys::ScopedWriter guard(m_mutex); - if (!m_dyld_image_retain_4HWTrace || !m_dyld_image_release_4HWTrace || - !m_dyld_image_segment_data_4HWTrace) + const LazyDyldSPIs &dyld = GetLazyDyldSPIs(); + if (!dyld.Has4HWTraceSPIs()) return false; if (filepath.empty()) @@ -917,7 +952,7 @@ static dispatch_data_t (*g_dyld_image_segment_data_4HWTrace)( LLDB_LOGF_VERBOSE(log, "sc file %s image %p", installname.GetCString(), (void *)image); - m_dyld_image_retain_4HWTrace(image); + dyld.image_retain_4HWTrace(image); m_file_infos[sc_uuid].push_back(SharedCacheImageInfo( installname, image_uuid, map_shared_cache_binary_segments, image)); }); @@ -955,29 +990,33 @@ static dispatch_data_t (*g_dyld_image_segment_data_4HWTrace)( return false; } -// Index the binaries in lldb's own shared cache memory, using -// libdyld SPI present on macOS 12 and newer, when building against -// the internal SDK, and add an entry to the m_caches map. +// Index the binaries in lldb's own shared cache memory, using the process +// snapshot libdyld SPIs present on macOS 12 and newer. The SPIs are resolved +// at runtime, so this works regardless of which SDK lldb was built against; +// it returns false when the running dyld doesn't vend them. bool SharedCacheInfo::CreateSharedCacheInfoWithInstrospectionSPIs() { llvm::sys::ScopedWriter guard(m_mutex); -#if defined(SDK_HAS_NEW_DYLD_INTROSPECTION_SPIS) - dyld_process_t dyld_process = dyld_process_create_for_current_task(); + const LazyDyldSPIs &dyld = GetLazyDyldSPIs(); + if (!dyld.HasProcessSnapshotSPIs()) + return false; + + dyld_process_t dyld_process = dyld.process_create_for_current_task(); if (!dyld_process) return false; llvm::scope_exit cleanup_process_on_exit( - [&]() { dyld_process_dispose(dyld_process); }); + [&]() { dyld.process_dispose(dyld_process); }); dyld_process_snapshot_t snapshot = - dyld_process_snapshot_create_for_process(dyld_process, nullptr); + dyld.process_snapshot_create_for_process(dyld_process, nullptr); if (!snapshot) return false; llvm::scope_exit cleanup_snapshot_on_exit( - [&]() { dyld_process_snapshot_dispose(snapshot); }); + [&]() { dyld.process_snapshot_dispose(snapshot); }); dyld_shared_cache_t shared_cache = - dyld_process_snapshot_get_shared_cache(snapshot); + dyld.process_snapshot_get_shared_cache(snapshot); if (!shared_cache) return false; @@ -1018,8 +1057,6 @@ static dispatch_data_t (*g_dyld_image_segment_data_4HWTrace)( m_uuid_map[m_host_uuid][entry->GetUUID()] = i; } return true; -#endif - return false; } // Index the binaries in lldb's own shared cache memory using >From 68ff27dea0279e6c896fb613b366b2cf1824f259 Mon Sep 17 00:00:00 2001 From: Jonas Devlieghere <[email protected]> Date: Tue, 23 Jun 2026 15:54:57 -0700 Subject: [PATCH 2/2] Drop CreateSharedCacheInfoLLDBsVirtualMemory fallback --- .../Host/macosx/objcxx/HostInfoMacOSX.mm | 42 +------------------ 1 file changed, 1 insertion(+), 41 deletions(-) diff --git a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm index 6166843f2e597..75a584d90121a 100644 --- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm +++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm @@ -779,7 +779,6 @@ SharedCacheImageInfo GetByUUID(UUID sc_uuid, UUID file_uuid) { private: bool CreateSharedCacheInfoWithInstrospectionSPIs(); - void CreateSharedCacheInfoLLDBsVirtualMemory(); bool CreateHostSharedCacheImageList(); // These three ivars have an initial key of a shared cache UUID. @@ -815,11 +814,7 @@ SharedCacheImageInfo GetByUUID(UUID sc_uuid, UUID file_uuid) { // Otherwise scan lldb's own shared cache, preferring the process snapshot // SPIs when the running dyld vends them. - if (CreateSharedCacheInfoWithInstrospectionSPIs()) - return; - - // Fall back to walking the shared cache mapping directly. - CreateSharedCacheInfoLLDBsVirtualMemory(); + CreateSharedCacheInfoWithInstrospectionSPIs(); } struct segment { @@ -1059,41 +1054,6 @@ static DataExtractorSP map_shared_cache_binary_segments(void *image) { return true; } -// Index the binaries in lldb's own shared cache memory using -// libdyld SPI available on macOS 10.13 or newer, add an entry to -// m_caches. -void SharedCacheInfo::CreateSharedCacheInfoLLDBsVirtualMemory() { - llvm::sys::ScopedWriter guard(m_mutex); - size_t shared_cache_size; - uint8_t *shared_cache_start = - _dyld_get_shared_cache_range(&shared_cache_size); - - // In macOS 26, a shared cache has around 3500 files. - m_file_infos[m_host_uuid].reserve(4000); - - dyld_shared_cache_iterate_text( - m_host_uuid.GetBytes().data(), - ^(const dyld_shared_cache_dylib_text_info *info) { - lldb::DataBufferSP buffer_sp = std::make_shared<DataBufferUnowned>( - shared_cache_start + info->textSegmentOffset, - shared_cache_size - info->textSegmentOffset); - lldb::DataExtractorSP extractor_sp = - std::make_shared<DataExtractor>(buffer_sp); - ConstString filepath(info->path); - m_file_infos[m_host_uuid].push_back(SharedCacheImageInfo( - filepath, UUID(info->dylibUuid, 16), extractor_sp)); - }); - - // std::vector of SharedCacheImageInfos has been fully populated, we can - // take pointers to the objects now. - size_t file_info_size = m_file_infos[m_host_uuid].size(); - for (size_t i = 0; i < file_info_size; i++) { - SharedCacheImageInfo *entry = &m_file_infos[m_host_uuid][i]; - m_filename_map[m_host_uuid][entry->GetFilename()] = i; - m_uuid_map[m_host_uuid][entry->GetUUID()] = i; - } -} - SharedCacheInfo &GetSharedCacheSingleton(SymbolSharedCacheUse sc_mode) { static SharedCacheInfo g_shared_cache_info(sc_mode); return g_shared_cache_info; _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
