[Lldb-commits] [lldb] [lldb] Strip pointer metadata in ReadMemoryRanges (PR #200398)

Felipe de Azevedo Piovezan via lldb-commits lldb-commits at lists.llvm.org
Mon Jun 1 01:09:33 PDT 2026


https://github.com/felipepiovezan updated https://github.com/llvm/llvm-project/pull/200398

>From 855d4787d1f30d100213fbabacc82221182d7beb Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Fri, 29 May 2026 13:41:32 +0100
Subject: [PATCH 1/2] [lldb] Strip pointer metadata in ReadMemoryRanges

The Process base class is generally responsible for fixing pointer
metadata before delegating memory reads to concrete Process
specializations. However, ReadMemoryRanges was a direct path into the
derived classes, which made it so that pointer metadata was never
stripped.

This commit creates a non-virtual ReadMemoryRanges in Process, which
clears pointer metadata, before delegating to the new virtual method
DoReadMemoryRanges. This also allows, in the future, to plug into the
memory cache system.
---
 lldb/include/lldb/Target/Process.h            | 12 ++-
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 10 +--
 .../Process/gdb-remote/ProcessGDBRemote.h     |  8 +-
 lldb/source/Target/Process.cpp                | 11 +++
 lldb/unittests/Target/MemoryTest.cpp          | 88 ++++++++++++++++++-
 5 files changed, 115 insertions(+), 14 deletions(-)

diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h
index e99b71399d01d..f68ea3b639e93 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -1619,9 +1619,6 @@ class Process : public std::enable_shared_from_this<Process>,
                             Status &error);
 
   /// Read from multiple memory ranges and write the results into buffer.
-  /// This calls ReadMemoryFromInferior multiple times, once per range,
-  /// bypassing the read cache. Process implementations that can perform this
-  /// operation more efficiently should override this.
   ///
   /// \param[in] ranges
   ///     A collection of ranges (base address + size) to read from.
@@ -1636,7 +1633,7 @@ class Process : public std::enable_shared_from_this<Process>,
   ///     of the slice indicates how many bytes were read successfully. Partial
   ///     reads are always performed from the start of the requested range,
   ///     never from the middle or end.
-  virtual llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
+  llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
   ReadMemoryRanges(llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges,
                    llvm::MutableArrayRef<uint8_t> buffer);
 
@@ -3047,6 +3044,13 @@ void PruneThreadPlans();
   virtual size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
                               Status &error) = 0;
 
+  /// Reads each range individually via ReadMemoryFromInferior, bypassing the
+  /// memory cache. Subclasses may override it to batch the reads more
+  /// efficiently.
+  virtual llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
+  DoReadMemoryRanges(llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges,
+                     llvm::MutableArrayRef<uint8_t> buffer);
+
   virtual void DoFindInMemory(lldb::addr_t start_addr, lldb::addr_t end_addr,
                               const uint8_t *buf, size_t size,
                               AddressRanges &matches, size_t alignment,
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 109cdd8f8dede..f6eaf5851338b 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -2890,11 +2890,11 @@ static uint64_t ComputeNumRangesMultiMemRead(
 }
 
 llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
-ProcessGDBRemote::ReadMemoryRanges(
+ProcessGDBRemote::DoReadMemoryRanges(
     llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges,
     llvm::MutableArrayRef<uint8_t> buffer) {
   if (!m_gdb_comm.GetMultiMemReadSupported())
-    return Process::ReadMemoryRanges(ranges, buffer);
+    return Process::DoReadMemoryRanges(ranges, buffer);
 
   const llvm::ArrayRef<Range<lldb::addr_t, size_t>> original_ranges = ranges;
   llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> memory_regions;
@@ -2903,7 +2903,7 @@ ProcessGDBRemote::ReadMemoryRanges(
     uint64_t num_ranges =
         ComputeNumRangesMultiMemRead(m_max_memory_size, ranges);
     if (num_ranges == 0)
-      return Process::ReadMemoryRanges(original_ranges, buffer);
+      return Process::DoReadMemoryRanges(original_ranges, buffer);
 
     auto ranges_for_request = ranges.take_front(num_ranges);
     ranges = ranges.drop_front(num_ranges);
@@ -2913,7 +2913,7 @@ ProcessGDBRemote::ReadMemoryRanges(
     if (!response) {
       LLDB_LOG_ERROR(GetLog(GDBRLog::Process), response.takeError(),
                      "MultiMemRead error response: {0}");
-      return Process::ReadMemoryRanges(original_ranges, buffer);
+      return Process::DoReadMemoryRanges(original_ranges, buffer);
     }
 
     llvm::StringRef response_str = response->GetStringRef();
@@ -2922,7 +2922,7 @@ ProcessGDBRemote::ReadMemoryRanges(
             response_str, buffer, expected_num_ranges, memory_regions)) {
       LLDB_LOG_ERROR(GetLog(GDBRLog::Process), std::move(error),
                      "MultiMemRead error parsing response: {0}");
-      return Process::ReadMemoryRanges(original_ranges, buffer);
+      return Process::DoReadMemoryRanges(original_ranges, buffer);
     }
   }
   return memory_regions;
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 921dc065ee560..0b71b304885c2 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -139,11 +139,11 @@ class ProcessGDBRemote : public Process,
   size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
                       Status &error) override;
 
-  /// Override of ReadMemoryRanges that uses MultiMemRead to optimize this
-  /// operation.
+  /// Override of DoReadMemoryRanges that uses MultiMemRead to perform this
+  /// operation in a single packet.
   llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
-  ReadMemoryRanges(llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges,
-                   llvm::MutableArrayRef<uint8_t> buf) override;
+  DoReadMemoryRanges(llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges,
+                     llvm::MutableArrayRef<uint8_t> buf) override;
 
 private:
   llvm::Expected<StringExtractorGDBRemote>
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 83c8f434e9a57..e77cb0b0835e1 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -2072,6 +2072,17 @@ size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error) {
 llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
 Process::ReadMemoryRanges(llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges,
                           llvm::MutableArrayRef<uint8_t> buffer) {
+  llvm::SmallVector<Range<lldb::addr_t, size_t>> fixed_ranges;
+  fixed_ranges.reserve(ranges.size());
+  for (const Range<lldb::addr_t, size_t> &range : ranges)
+    fixed_ranges.emplace_back(FixAnyAddress(range.GetRangeBase()),
+                              range.GetByteSize());
+  return DoReadMemoryRanges(fixed_ranges, buffer);
+}
+
+llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
+Process::DoReadMemoryRanges(llvm::ArrayRef<Range<lldb::addr_t, size_t>> ranges,
+                            llvm::MutableArrayRef<uint8_t> buffer) {
   auto total_ranges_len = llvm::sum_of(
       llvm::map_range(ranges, [](auto range) { return range.size; }));
   // If the buffer is not large enough, this is a programmer error.
diff --git a/lldb/unittests/Target/MemoryTest.cpp b/lldb/unittests/Target/MemoryTest.cpp
index 21045725be84a..2fa097808abbb 100644
--- a/lldb/unittests/Target/MemoryTest.cpp
+++ b/lldb/unittests/Target/MemoryTest.cpp
@@ -10,8 +10,10 @@
 #include "Plugins/Platform/MacOSX/PlatformMacOSX.h"
 #include "Plugins/Platform/MacOSX/PlatformRemoteMacOSX.h"
 #include "lldb/Core/Debugger.h"
+#include "lldb/Core/PluginManager.h"
 #include "lldb/Host/FileSystem.h"
 #include "lldb/Host/HostInfo.h"
+#include "lldb/Target/ABI.h"
 #include "lldb/Target/Process.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Utility/ArchSpec.h"
@@ -23,17 +25,56 @@ using namespace lldb_private;
 using namespace lldb;
 
 namespace {
+class MockABI : public ABI {
+public:
+  // The only relevant method of this ABI:
+  lldb::addr_t FixAnyAddress(lldb::addr_t pc) override {
+    return pc & 0xf0ffffffffffffffULL;
+  }
+
+  explicit MockABI(ProcessSP process_sp)
+      : ABI(std::move(process_sp), std::make_unique<llvm::MCRegisterInfo>()) {}
+  static ABISP CreateInstance(ProcessSP process_sp, const ArchSpec &) {
+    return std::make_shared<MockABI>(std::move(process_sp));
+  }
+  llvm::StringRef GetPluginName() override { return "mock"; }
+  size_t GetRedZoneSize() const override { return 0; }
+  bool PrepareTrivialCall(Thread &, addr_t, addr_t, addr_t,
+                          llvm::ArrayRef<addr_t>) const override {
+    return false;
+  }
+  bool GetArgumentValues(Thread &, ValueList &) const override { return false; }
+  Status SetReturnValueObject(StackFrameSP &, ValueObjectSP &) override {
+    return {};
+  }
+  UnwindPlanSP CreateFunctionEntryUnwindPlan() override { return nullptr; }
+  UnwindPlanSP CreateDefaultUnwindPlan() override { return nullptr; }
+  bool RegisterIsVolatile(const RegisterInfo *) override { return false; }
+  bool CallFrameAddressIsValid(addr_t) override { return false; }
+  bool CodeAddressIsValid(addr_t) override { return false; }
+  void
+  AugmentRegisterInfo(std::vector<DynamicRegisterInfo::Register> &) override {}
+
+protected:
+  ValueObjectSP GetReturnValueObjectImpl(Thread &,
+                                         CompilerType &) const override {
+    return nullptr;
+  }
+};
+
 class MemoryTest : public ::testing::Test {
 public:
   void SetUp() override {
     FileSystem::Initialize();
     HostInfo::Initialize();
     PlatformMacOSX::Initialize();
+    PluginManager::RegisterPlugin("mock", "mock ABI", MockABI::CreateInstance);
   }
   void TearDown() override {
     PlatformMacOSX::Terminate();
     HostInfo::Terminate();
     FileSystem::Terminate();
+    PluginManager::UnregisterPlugin(MockABI::CreateInstance);
   }
 };
 
@@ -295,7 +336,7 @@ TEST_F(MemoryTest, TestReadInteger) {
 }
 
 /// A process class that, when asked to read memory from some address X, returns
-/// the least significant byte of X.
+/// the most or least significant byte of X, depending on how it is configured.
 class DummyReaderProcess : public Process {
 public:
   // If true, `DoReadMemory` will not return all requested bytes.
@@ -616,3 +657,48 @@ TEST_F(MemoryTest, TestReadUnsignedIntegersFromMemory) {
     }
   }
 }
+
+// A process that, when asked to read memory from address X, returns the top
+// byte of X.
+class DummyMSBReaderProcess : public Process {
+public:
+  // Only call this method with exactly one range.
+  llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
+  DoReadMemoryRanges(llvm::ArrayRef<Range<addr_t, size_t>> ranges,
+                     llvm::MutableArrayRef<uint8_t> buffer) override {
+    buffer[0] = static_cast<uint8_t>(ranges[0].GetRangeBase() >> 56);
+    return {{buffer.take_front(1)}};
+  }
+  // Boilerplate, nothing interesting below.
+  DummyMSBReaderProcess(TargetSP target_sp, ListenerSP listener_sp)
+      : Process(target_sp, listener_sp) {}
+  bool CanDebug(TargetSP, bool) override { return true; }
+  Status DoDestroy() override { return {}; }
+  void RefreshStateAfterStop() override {}
+  bool DoUpdateThreadList(ThreadList &, ThreadList &) override { return false; }
+  llvm::StringRef GetPluginName() override { return "Dummy"; }
+  size_t DoReadMemory(addr_t, void *, size_t, Status &) override {
+    llvm_unreachable("don't call this");
+  }
+};
+
+TEST_F(MemoryTest, TestReadMemoryRangesClearMetadata) {
+  ArchSpec arch("x86_64-apple-macosx-");
+
+  Platform::SetHostPlatform(PlatformRemoteMacOSX::CreateInstance(true, &arch));
+  DebuggerSP debugger_sp = Debugger::CreateInstance();
+  ASSERT_TRUE(debugger_sp);
+  TargetSP target_sp = CreateTarget(debugger_sp, arch);
+  ASSERT_TRUE(target_sp);
+  ListenerSP listener_sp(Listener::MakeListener("dummy"));
+  ProcessSP process_sp =
+      std::make_shared<DummyMSBReaderProcess>(target_sp, listener_sp);
+
+  llvm::SmallVector<uint8_t, 0> buffer(1024, 0);
+  llvm::SmallVector<Range<addr_t, size_t>> ranges = {{0xff0123456789abcd, 1}};
+  llvm::SmallVector<llvm::MutableArrayRef<uint8_t>> read_results =
+      process_sp->ReadMemoryRanges(ranges, buffer);
+  ASSERT_EQ(read_results.size(), 1ull);
+  ASSERT_EQ(read_results[0].size(), 1ull);
+  ASSERT_EQ(read_results[0][0], 0xf0); // The ABI masks with 0xf0.
+}

>From 70470ce4f27cd374bc9b4edddff13dd113f53f87 Mon Sep 17 00:00:00 2001
From: Felipe de Azevedo Piovezan <fpiovezan at apple.com>
Date: Mon, 1 Jun 2026 09:09:17 +0100
Subject: [PATCH 2/2] fixup! remove incorrect comment

---
 lldb/unittests/Target/MemoryTest.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lldb/unittests/Target/MemoryTest.cpp b/lldb/unittests/Target/MemoryTest.cpp
index 2fa097808abbb..e2a125e7e10a9 100644
--- a/lldb/unittests/Target/MemoryTest.cpp
+++ b/lldb/unittests/Target/MemoryTest.cpp
@@ -336,7 +336,7 @@ TEST_F(MemoryTest, TestReadInteger) {
 }
 
 /// A process class that, when asked to read memory from some address X, returns
-/// the most or least significant byte of X, depending on how it is configured.
+/// the least significant byte of X.
 class DummyReaderProcess : public Process {
 public:
   // If true, `DoReadMemory` will not return all requested bytes.



More information about the lldb-commits mailing list