[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