[Lldb-commits] [lldb] 8204f7a - [lldb] Fix shared cache segment mapping leak (#232370)

via lldb-commits lldb-commits at lists.llvm.org
Tue Oct 20 14:50:03 PDT 2026


Author: Jonas Devlieghere
Date: 2026-10-20T14:49:58-07:00
New Revision: 8204f7a364d4c54149ae22acfc184ee4cb0ab3eb

URL: https://github.com/llvm/llvm-project/commit/8204f7a364d4c54149ae22acfc184ee4cb0ab3eb
DIFF: https://github.com/llvm/llvm-project/commit/8204f7a364d4c54149ae22acfc184ee4cb0ab3eb.diff

LOG: [lldb] Fix shared cache segment mapping leak (#232370)

`map_shared_cache_binary_segments` never released the dispatch_data
returned by dispatch_data_create_map. SharedCacheInfo also returned
copies of SharedCacheImageInfo, so the extractor cached by
GetExtractor() was dropped and every lookup mapped the image again.

Keep the mappings alive in a DataBuffer that releases them on
destruction, and share a lazily created extractor between copies so each
image is mapped once.

rdar://189874911

Assisted-by: Claude

Added: 
    

Modified: 
    lldb/include/lldb/Host/HostInfoBase.h
    lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
    lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp

Removed: 
    


################################################################################
diff  --git a/lldb/include/lldb/Host/HostInfoBase.h b/lldb/include/lldb/Host/HostInfoBase.h
index f06cdd59b020a..ba31b60f76bfe 100644
--- a/lldb/include/lldb/Host/HostInfoBase.h
+++ b/lldb/include/lldb/Host/HostInfoBase.h
@@ -18,9 +18,11 @@
 #include "lldb/lldb-enumerations.h"
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Errc.h"
+#include "llvm/Support/Threading.h"
 
 #include <cstdint>
 
+#include <memory>
 #include <optional>
 #include <string>
 
@@ -31,41 +33,45 @@ class FileSpec;
 struct SharedCacheImageInfo {
   SharedCacheImageInfo()
       : m_filename(), m_uuid(), m_extractor_sp(),
-        m_create_data_extractor(nullptr), m_image_baton(nullptr) {}
+        m_create_data_extractor(nullptr), m_image_baton(nullptr), m_lazy() {}
   SharedCacheImageInfo(std::string filename, UUID uuid,
                        lldb::DataExtractorSP extractor_sp)
       : m_filename(std::move(filename)), m_uuid(uuid),
         m_extractor_sp(extractor_sp), m_create_data_extractor(nullptr),
-        m_image_baton(nullptr) {}
+        m_image_baton(nullptr), m_lazy() {}
   SharedCacheImageInfo(
       std::string filename, UUID uuid,
       lldb::DataExtractorSP (*create_data_extractor)(void *image),
       void *image_baton)
       : m_filename(std::move(filename)), m_uuid(uuid), m_extractor_sp(),
         m_create_data_extractor(create_data_extractor),
-        m_image_baton(image_baton) {}
+        m_image_baton(image_baton), m_lazy(std::make_shared<LazyExtractor>()) {}
 
+  /// Copies share the extractor, which is created at most once.
   lldb::DataExtractorSP GetExtractor() {
-    if (!m_extractor_sp && m_image_baton)
-      m_extractor_sp = m_create_data_extractor(m_image_baton);
+    if (m_lazy) {
+      llvm::call_once(m_lazy->once, [&] {
+        m_lazy->extractor_sp = m_create_data_extractor(m_image_baton);
+      });
+      return m_lazy->extractor_sp;
+    }
     return m_extractor_sp;
   }
   llvm::StringRef GetFilename() const { return m_filename; }
   const UUID &GetUUID() const { return m_uuid; }
-  void *GetImageBaton();
-  void SetExtractor(lldb::DataExtractorSP extractor_sp) {
-    m_extractor_sp = extractor_sp;
-  }
-  void SetImageBaton(void *image_baton) { m_image_baton = image_baton; }
-  void SetDataExtractorCreateFunction(
-      lldb::DataExtractorSP (*create_data_extractor)(void *image));
 
 private:
+  struct LazyExtractor {
+    llvm::once_flag once;
+    lldb::DataExtractorSP extractor_sp;
+  };
+
   std::string m_filename;
   UUID m_uuid;
   lldb::DataExtractorSP m_extractor_sp;
   lldb::DataExtractorSP (*m_create_data_extractor)(void *image);
   void *m_image_baton;
+  std::shared_ptr<LazyExtractor> m_lazy;
 };
 
 namespace {

diff  --git a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
index a2859a8788dbb..81cd5c468732a 100644
--- a/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
+++ b/lldb/source/Host/macosx/objcxx/HostInfoMacOSX.mm
@@ -818,15 +818,36 @@ SharedCacheImageInfo GetByUUID(UUID sc_uuid, UUID file_uuid) {
   CreateSharedCacheInfoWithInstrospectionSPIs();
 }
 
+namespace {
 struct segment {
   std::string name;
   uint64_t vmaddr;
   size_t vmsize;
 
   // Mapped into lldb's own address space via libdispatch:
-  const void *data;
-  size_t size;
+  dispatch_data_t mapping = nullptr;
+  const void *data = nullptr;
+  size_t size = 0;
+};
+
+/// An unowned view of an image's segments that keeps their mappings alive.
+class DataBufferDispatchData : public DataBufferUnowned {
+public:
+  DataBufferDispatchData(uint8_t *bytes, lldb::offset_t size,
+                         std::vector<dispatch_data_t> mappings)
+      : DataBufferUnowned(bytes, size), m_mappings(std::move(mappings)) {}
+  DataBufferDispatchData(const DataBufferDispatchData &) = delete;
+  DataBufferDispatchData &operator=(const DataBufferDispatchData &) = delete;
+
+  ~DataBufferDispatchData() override {
+    for (dispatch_data_t mapping : m_mappings)
+      dispatch_release(mapping);
+  }
+
+private:
+  std::vector<dispatch_data_t> m_mappings;
 };
+} // namespace
 
 static DataExtractorSP map_shared_cache_binary_segments(void *image) {
   // dyld_image_segment_data_4HWTrace can't be called on
@@ -848,12 +869,20 @@ static DataExtractorSP map_shared_cache_binary_segments(void *image) {
         seg.vmaddr = vmAddr;
         seg.vmsize = vmSize;
 
-        dispatch_data_t data_from_libdyld =
-            dyld.image_segment_data_4HWTrace(image_copy, segmentName);
-        (void)dispatch_data_create_map(data_from_libdyld, &seg.data, &seg.size);
+        // The segment data is autoreleased. The map is ours to release.
+        @autoreleasepool {
+          dispatch_data_t data_from_libdyld =
+              dyld.image_segment_data_4HWTrace(image_copy, segmentName);
+          // NULL when the cache's backing file is unavailable.
+          if (data_from_libdyld)
+            seg.mapping = dispatch_data_create_map(data_from_libdyld, &seg.data,
+                                                   &seg.size);
+        }
 
         if (seg.size > 0 && seg.data != 0)
           segments.push_back(seg);
+        else if (seg.mapping)
+          dispatch_release(seg.mapping);
       });
 
   if (!segments.size())
@@ -886,8 +915,12 @@ static DataExtractorSP map_shared_cache_binary_segments(void *image) {
         std::max(max_lldb_vm_addr, (uint64_t)seg.data + seg.vmsize);
     min_file_vm_addr = std::min(min_file_vm_addr, (uint64_t)seg.vmaddr);
   }
-  DataBufferSP data_sp = std::make_shared<DataBufferUnowned>(
-      (uint8_t *)min_lldb_vm_addr, max_lldb_vm_addr - min_lldb_vm_addr);
+  std::vector<dispatch_data_t> mappings;
+  for (const segment &seg : segments)
+    mappings.push_back(seg.mapping);
+  DataBufferSP data_sp = std::make_shared<DataBufferDispatchData>(
+      (uint8_t *)min_lldb_vm_addr, max_lldb_vm_addr - min_lldb_vm_addr,
+      std::move(mappings));
   VirtualDataExtractor::LookupTable remap_table;
   for (const segment &seg : segments)
     remap_table.Append(VirtualDataExtractor::LookupTable::Entry(

diff  --git a/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp b/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp
index a9a24b855c52e..6963cc0b791bd 100644
--- a/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp
+++ b/lldb/unittests/ObjectFile/MachO/TestObjectFileMachO.cpp
@@ -91,6 +91,18 @@ TEST_F(ObjectFileMachOTest, ModuleFromSharedCacheInfo) {
   check_symbol("OBJC_IVAR_$_NSObject.isa");
 }
 
+TEST_F(ObjectFileMachOTest, SharedCacheImageExtractorIsShared) {
+  auto get_extractor = [] {
+    return HostInfo::GetSharedCacheImageInfo(
+               "/usr/lib/libobjc.A.dylib",
+               lldb::eSymbolSharedCacheUseHostSharedCache)
+        .GetExtractor();
+  };
+  lldb::DataExtractorSP extractor_sp = get_extractor();
+  ASSERT_TRUE(extractor_sp);
+  EXPECT_EQ(extractor_sp, get_extractor());
+}
+
 TEST_F(ObjectFileMachOTest, IndirectSymbolsInTheSharedCache) {
   SharedCacheImageInfo image_info = HostInfo::GetSharedCacheImageInfo(
       "/System/Library/Frameworks/AppKit.framework/Versions/C/AppKit",


        


More information about the lldb-commits mailing list