https://github.com/satyajanga updated 
https://github.com/llvm/llvm-project/pull/214089

>From 8e485f29ae53c1fbe9dacce4a0766816f201cb16 Mon Sep 17 00:00:00 2001
From: satya janga <[email protected]>
Date: Tue, 4 Aug 2026 12:21:36 -0700
Subject: [PATCH 1/2] [lldb] Adopt ProcessAddress in the read memory APIs (NFC)

Mechanical change: switch the read memory entry points from
lldb::addr_t to const ProcessAddress &, and update every override.

  Process::ReadMemory / Process::DoReadMemory
  NativeProcessProtocol::ReadMemory / ReadMemoryWithoutTrap

ProcessAddress is implicitly constructible from lldb::addr_t, so callers
are unchanged; only the virtual signatures and their overrides needed
updating. Each override extracts the address with GetValue() and keeps
its existing body, so behavior is identical. No address space is ever
non-default after this change.
---
 .../lldb/Host/common/NativeProcessProtocol.h    |  7 ++++---
 lldb/include/lldb/Target/Process.h              |  9 +++++----
 lldb/include/lldb/Target/ProcessTrace.h         |  4 ++--
 .../Host/common/NativeProcessProtocol.cpp       | 10 ++++++----
 .../Plugins/Process/AIX/NativeProcessAIX.cpp    |  3 ++-
 .../Plugins/Process/AIX/NativeProcessAIX.h      |  2 +-
 .../ProcessFreeBSDKernelCore.cpp                |  6 ++++--
 .../ProcessFreeBSDKernelCore.h                  |  4 ++--
 .../Process/FreeBSD/NativeProcessFreeBSD.cpp    |  6 ++++--
 .../Process/FreeBSD/NativeProcessFreeBSD.h      |  2 +-
 .../Process/Linux/NativeProcessLinux.cpp        |  4 +++-
 .../Plugins/Process/Linux/NativeProcessLinux.h  |  2 +-
 .../Process/MacOSX-Kernel/ProcessKDP.cpp        |  5 +++--
 .../Plugins/Process/MacOSX-Kernel/ProcessKDP.h  |  4 ++--
 .../Process/NetBSD/NativeProcessNetBSD.cpp      |  6 ++++--
 .../Process/NetBSD/NativeProcessNetBSD.h        |  2 +-
 .../Windows/Common/NativeProcessWindows.cpp     |  6 ++++--
 .../Windows/Common/NativeProcessWindows.h       |  2 +-
 .../Process/Windows/Common/ProcessWindows.cpp   |  5 +++--
 .../Process/Windows/Common/ProcessWindows.h     |  2 +-
 .../Plugins/Process/elf-core/ProcessElfCore.cpp | 10 ++++++----
 .../Plugins/Process/elf-core/ProcessElfCore.h   |  8 ++++----
 .../Process/gdb-remote/ProcessGDBRemote.cpp     |  5 +++--
 .../Process/gdb-remote/ProcessGDBRemote.h       |  4 ++--
 .../Process/mach-core/ProcessMachCore.cpp       | 10 ++++++----
 .../Plugins/Process/mach-core/ProcessMachCore.h |  8 ++++----
 .../Process/minidump/ProcessMinidump.cpp        | 10 ++++++----
 .../Plugins/Process/minidump/ProcessMinidump.h  |  4 ++--
 .../Process/scripted/ScriptedProcess.cpp        |  5 +++--
 .../Plugins/Process/scripted/ScriptedProcess.h  |  2 +-
 .../source/Plugins/Process/wasm/ProcessWasm.cpp |  5 +++--
 lldb/source/Plugins/Process/wasm/ProcessWasm.h  |  2 +-
 lldb/source/Target/Process.cpp                  |  4 +++-
 lldb/source/Target/ProcessTrace.cpp             | 10 ++++++----
 .../Accelerator/Mock/ProcessMockAccelerator.cpp |  5 +++--
 .../Accelerator/Mock/ProcessMockAccelerator.h   |  2 +-
 .../DataFormatter/FormatterSectionTest.cpp      |  4 ++--
 .../Expression/DWARFExpressionTest.cpp          |  9 +++++----
 lldb/unittests/Expression/IRMemoryMapTest.cpp   |  4 ++--
 lldb/unittests/Process/ProcessEventDataTest.cpp |  4 ++--
 .../Process/elf-core/ThreadElfCoreTest.cpp      |  4 ++--
 lldb/unittests/Target/ExecutionContextTest.cpp  |  4 ++--
 .../Target/LocateModuleCallbackTest.cpp         |  4 ++--
 lldb/unittests/Target/MemoryTest.cpp            | 17 ++++++++++-------
 .../Host/NativeProcessTestUtils.h               |  3 ++-
 lldb/unittests/Thread/ThreadTest.cpp            |  4 ++--
 .../ValueObject/DumpValueObjectOptionsTests.cpp |  4 ++--
 .../DynamicValueObjectLocalBuffer.cpp           |  4 ++--
 48 files changed, 143 insertions(+), 107 deletions(-)

diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h 
b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 435185a38f3f9..67206c4b55b79 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -16,6 +16,7 @@
 #include "lldb/Host/MainLoop.h"
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/Iterable.h"
+#include "lldb/Utility/ProcessAddress.h"
 #include "lldb/Utility/Status.h"
 #include "lldb/Utility/TraceGDBRemotePackets.h"
 #include "lldb/Utility/UnimplementedError.h"
@@ -96,11 +97,11 @@ class NativeProcessProtocol {
   virtual Status GetMemoryRegionInfo(lldb::addr_t load_addr,
                                      MemoryRegionInfo &range_info);
 
-  virtual Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+  virtual Status ReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                             size_t &bytes_read) = 0;
 
-  Status ReadMemoryWithoutTrap(lldb::addr_t addr, void *buf, size_t size,
-                               size_t &bytes_read);
+  Status ReadMemoryWithoutTrap(const ProcessAddress &addr, void *buf,
+                               size_t size, size_t &bytes_read);
 
   virtual Status ReadMemoryTags(int32_t type, lldb::addr_t addr, size_t len,
                                 std::vector<uint8_t> &tags);
diff --git a/lldb/include/lldb/Target/Process.h 
b/lldb/include/lldb/Target/Process.h
index 9162158a277d9..f4902ceed1b8a 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -54,6 +54,7 @@
 #include "lldb/Utility/Listener.h"
 #include "lldb/Utility/NameMatches.h"
 #include "lldb/Utility/Policy.h"
+#include "lldb/Utility/ProcessAddress.h"
 #include "lldb/Utility/ProcessInfo.h"
 #include "lldb/Utility/Status.h"
 #include "lldb/Utility/StructuredData.h"
@@ -1629,8 +1630,8 @@ class Process : public 
std::enable_shared_from_this<Process>,
   ///     size, then this function will get called again with \a
   ///     vm_addr, \a buf, and \a size updated appropriately. Zero is
   ///     returned in the case of an error.
-  virtual size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
-                            Status &error);
+  virtual size_t ReadMemory(const ProcessAddress &process_addr, void *buf,
+                            size_t size, Status &error);
 
   /// Read from multiple memory ranges and write the results into buffer.
   ///
@@ -3051,8 +3052,8 @@ void PruneThreadPlans();
   /// \return
   ///     The number of bytes that were actually read into \a buf.
   ///     Zero is returned in the case of an error.
-  virtual size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
-                              Status &error) = 0;
+  virtual size_t DoReadMemory(const ProcessAddress &process_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
diff --git a/lldb/include/lldb/Target/ProcessTrace.h 
b/lldb/include/lldb/Target/ProcessTrace.h
index 50237c2af9189..2c7df58137330 100644
--- a/lldb/include/lldb/Target/ProcessTrace.h
+++ b/lldb/include/lldb/Target/ProcessTrace.h
@@ -54,10 +54,10 @@ class ProcessTrace : public PostMortemProcess {
 
   bool WarnBeforeDetach() const override { return false; }
 
-  size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+  size_t ReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                     Status &error) override;
 
-  size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+  size_t DoReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                       Status &error) override;
 
   ArchSpec GetArchitecture();
diff --git a/lldb/source/Host/common/NativeProcessProtocol.cpp 
b/lldb/source/Host/common/NativeProcessProtocol.cpp
index dbffdc619ef42..8c5991e93aab3 100644
--- a/lldb/source/Host/common/NativeProcessProtocol.cpp
+++ b/lldb/source/Host/common/NativeProcessProtocol.cpp
@@ -649,13 +649,15 @@ Status 
NativeProcessProtocol::RemoveBreakpoint(lldb::addr_t addr,
     return RemoveSoftwareBreakpoint(addr);
 }
 
-Status NativeProcessProtocol::ReadMemoryWithoutTrap(lldb::addr_t addr,
-                                                    void *buf, size_t size,
-                                                    size_t &bytes_read) {
-  Status error = ReadMemory(addr, buf, size, bytes_read);
+Status
+NativeProcessProtocol::ReadMemoryWithoutTrap(const ProcessAddress 
&process_addr,
+                                             void *buf, size_t size,
+                                             size_t &bytes_read) {
+  Status error = ReadMemory(process_addr, buf, size, bytes_read);
   if (error.Fail())
     return error;
 
+  lldb::addr_t addr = process_addr.GetValue();
   llvm::MutableArrayRef data(static_cast<uint8_t *>(buf), bytes_read);
   for (const auto &pair : m_software_breakpoints) {
     lldb::addr_t bp_addr = pair.first;
diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp 
b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp
index f39573e689c4f..de11641790655 100644
--- a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp
+++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp
@@ -238,7 +238,8 @@ Status NativeProcessAIX::Kill() {
   return error;
 }
 
-Status NativeProcessAIX::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+Status NativeProcessAIX::ReadMemory(const ProcessAddress &process_addr,
+                                    void *buf, size_t size,
                                     size_t &bytes_read) {
   return Status("unsupported");
 }
diff --git a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h 
b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h
index bc44f2b02af98..068b428e1c8a9 100644
--- a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h
+++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.h
@@ -77,7 +77,7 @@ class NativeProcessAIX : public NativeProcessProtocol {
 
   lldb::addr_t GetSharedLibraryInfoAddress() override;
 
-  Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+  Status ReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                     size_t &bytes_read) override;
 
   Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
diff --git 
a/lldb/source/Plugins/Process/FreeBSD-Kernel-Core/ProcessFreeBSDKernelCore.cpp 
b/lldb/source/Plugins/Process/FreeBSD-Kernel-Core/ProcessFreeBSDKernelCore.cpp
index 3ec46ea7fdf25..208069d7f9ff3 100644
--- 
a/lldb/source/Plugins/Process/FreeBSD-Kernel-Core/ProcessFreeBSDKernelCore.cpp
+++ 
b/lldb/source/Plugins/Process/FreeBSD-Kernel-Core/ProcessFreeBSDKernelCore.cpp
@@ -505,8 +505,10 @@ bool 
ProcessFreeBSDKernelCore::DoUpdateThreadList(ThreadList &old_thread_list,
   return new_thread_list.GetSize(false) > 0;
 }
 
-size_t ProcessFreeBSDKernelCore::DoReadMemory(lldb::addr_t addr, void *buf,
-                                              size_t size, Status &error) {
+size_t
+ProcessFreeBSDKernelCore::DoReadMemory(const ProcessAddress &process_addr,
+                                       void *buf, size_t size, Status &error) {
+  lldb::addr_t addr = process_addr.GetValue();
   ssize_t rd = 0;
   rd = kvm_read2(m_kvm, addr, buf, size);
   if (rd < 0 || static_cast<size_t>(rd) != size) {
diff --git 
a/lldb/source/Plugins/Process/FreeBSD-Kernel-Core/ProcessFreeBSDKernelCore.h 
b/lldb/source/Plugins/Process/FreeBSD-Kernel-Core/ProcessFreeBSDKernelCore.h
index c8355b3f5b56f..477ae77791e51 100644
--- a/lldb/source/Plugins/Process/FreeBSD-Kernel-Core/ProcessFreeBSDKernelCore.h
+++ b/lldb/source/Plugins/Process/FreeBSD-Kernel-Core/ProcessFreeBSDKernelCore.h
@@ -62,8 +62,8 @@ class ProcessFreeBSDKernelCore : public 
lldb_private::PostMortemProcess {
   bool DoUpdateThreadList(lldb_private::ThreadList &old_thread_list,
                           lldb_private::ThreadList &new_thread_list) override;
 
-  size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
-                      lldb_private::Status &error) override;
+  size_t DoReadMemory(const lldb_private::ProcessAddress &addr, void *buf,
+                      size_t size, lldb_private::Status &error) override;
 
   lldb::addr_t FindSymbol(const char *name);
 
diff --git a/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp 
b/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
index 0a68a14a6cb94..74fdda1fd7934 100644
--- a/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
+++ b/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.cpp
@@ -871,8 +871,10 @@ Status NativeProcessFreeBSD::Attach() {
   return Status();
 }
 
-Status NativeProcessFreeBSD::ReadMemory(lldb::addr_t addr, void *buf,
-                                        size_t size, size_t &bytes_read) {
+Status NativeProcessFreeBSD::ReadMemory(const ProcessAddress &process_addr,
+                                        void *buf, size_t size,
+                                        size_t &bytes_read) {
+  lldb::addr_t addr = process_addr.GetValue();
   unsigned char *dst = static_cast<unsigned char *>(buf);
   struct ptrace_io_desc io;
 
diff --git a/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h 
b/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
index 7e8bdc527f420..aecb7ab74d0f3 100644
--- a/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
+++ b/lldb/source/Plugins/Process/FreeBSD/NativeProcessFreeBSD.h
@@ -59,7 +59,7 @@ class NativeProcessFreeBSD : public NativeProcessELF {
   Status GetMemoryRegionInfo(lldb::addr_t load_addr,
                              MemoryRegionInfo &range_info) override;
 
-  Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+  Status ReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                     size_t &bytes_read) override;
 
   Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp 
b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
index 4f9dd4f30664d..d15907d7cedb7 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.cpp
@@ -1614,8 +1614,10 @@ 
NativeProcessLinux::GetSoftwareBreakpointTrapOpcode(size_t size_hint) {
   }
 }
 
-Status NativeProcessLinux::ReadMemory(lldb::addr_t addr, void *buf, size_t 
size,
+Status NativeProcessLinux::ReadMemory(const ProcessAddress &process_addr,
+                                      void *buf, size_t size,
                                       size_t &bytes_read) {
+  lldb::addr_t addr = process_addr.GetValue();
   Log *log = GetLog(POSIXLog::Memory);
   LLDB_LOG(log, "addr = {0}, buf = {1}, size = {2}", addr, buf, size);
 
diff --git a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h 
b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
index 936d690e42ae7..b45e5ff1546ef 100644
--- a/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
+++ b/lldb/source/Plugins/Process/Linux/NativeProcessLinux.h
@@ -96,7 +96,7 @@ class NativeProcessLinux : public NativeProcessELF,
   Status GetMemoryRegionInfo(lldb::addr_t load_addr,
                              MemoryRegionInfo &range_info) override;
 
-  Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+  Status ReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                     size_t &bytes_read) override;
 
   Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp 
b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
index 6166096a4e1d3..9a8f92979f370 100644
--- a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
+++ b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.cpp
@@ -577,8 +577,9 @@ bool ProcessKDP::IsAlive() {
 }
 
 // Process Memory
-size_t ProcessKDP::DoReadMemory(addr_t addr, void *buf, size_t size,
-                                Status &error) {
+size_t ProcessKDP::DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                                size_t size, Status &error) {
+  lldb::addr_t addr = process_addr.GetValue();
   uint8_t *data_buffer = (uint8_t *)buf;
   if (m_comm.IsConnected()) {
     const size_t max_read_size = 512;
diff --git a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h 
b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h
index 1b71d83f70b08..7b790b06a9cb9 100644
--- a/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h
+++ b/lldb/source/Plugins/Process/MacOSX-Kernel/ProcessKDP.h
@@ -106,8 +106,8 @@ class ProcessKDP : public lldb_private::Process {
   bool IsAlive() override;
 
   // Process Memory
-  size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
-                      lldb_private::Status &error) override;
+  size_t DoReadMemory(const lldb_private::ProcessAddress &addr, void *buf,
+                      size_t size, lldb_private::Status &error) override;
 
   size_t DoWriteMemory(lldb::addr_t addr, const void *buf, size_t size,
                        lldb_private::Status &error) override;
diff --git a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp 
b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
index 3cbb4dc96c1d4..5b2b80866c98c 100644
--- a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
+++ b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.cpp
@@ -899,8 +899,10 @@ Status NativeProcessNetBSD::Attach() {
   return Status();
 }
 
-Status NativeProcessNetBSD::ReadMemory(lldb::addr_t addr, void *buf,
-                                       size_t size, size_t &bytes_read) {
+Status NativeProcessNetBSD::ReadMemory(const ProcessAddress &process_addr,
+                                       void *buf, size_t size,
+                                       size_t &bytes_read) {
+  lldb::addr_t addr = process_addr.GetValue();
   unsigned char *dst = static_cast<unsigned char *>(buf);
   struct ptrace_io_desc io;
 
diff --git a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h 
b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
index 976d48e74854e..599943290c163 100644
--- a/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
+++ b/lldb/source/Plugins/Process/NetBSD/NativeProcessNetBSD.h
@@ -57,7 +57,7 @@ class NativeProcessNetBSD : public NativeProcessELF {
   Status GetMemoryRegionInfo(lldb::addr_t load_addr,
                              MemoryRegionInfo &range_info) override;
 
-  Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+  Status ReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                     size_t &bytes_read) override;
 
   Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
diff --git 
a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp 
b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
index f87fd23f5a047..081143fd32a2f 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
@@ -232,8 +232,10 @@ Status 
NativeProcessWindows::GetMemoryRegionInfo(lldb::addr_t load_addr,
   return ProcessDebugger::GetMemoryRegionInfo(load_addr, range_info);
 }
 
-Status NativeProcessWindows::ReadMemory(lldb::addr_t addr, void *buf,
-                                        size_t size, size_t &bytes_read) {
+Status NativeProcessWindows::ReadMemory(const ProcessAddress &process_addr,
+                                        void *buf, size_t size,
+                                        size_t &bytes_read) {
+  lldb::addr_t addr = process_addr.GetValue();
   return ProcessDebugger::ReadMemory(addr, buf, size, bytes_read);
 }
 
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h 
b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
index 17469f18fbc73..7801d6febd28a 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
@@ -68,7 +68,7 @@ class NativeProcessWindows : public NativeProcessProtocol,
   Status GetMemoryRegionInfo(lldb::addr_t load_addr,
                              MemoryRegionInfo &range_info) override;
 
-  Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+  Status ReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                     size_t &bytes_read) override;
 
   Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp 
b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 561710ccec3c8..0b29b898cd342 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -547,8 +547,9 @@ ArchSpec ProcessWindows::GetSystemArchitecture() {
   return HostInfo::GetArchitecture();
 }
 
-size_t ProcessWindows::DoReadMemory(lldb::addr_t vm_addr, void *buf,
-                                    size_t size, Status &error) {
+size_t ProcessWindows::DoReadMemory(const ProcessAddress &process_addr,
+                                    void *buf, size_t size, Status &error) {
+  lldb::addr_t vm_addr = process_addr.GetValue();
   size_t bytes_read = 0;
   error = ProcessDebugger::ReadMemory(vm_addr, buf, size, bytes_read);
   return bytes_read;
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h 
b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
index 2d2f3ca59ac70..73fd92f11cdff 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.h
@@ -69,7 +69,7 @@ class ProcessWindows : public Process, public ProcessDebugger 
{
 
   ArchSpec GetSystemArchitecture() override;
 
-  size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+  size_t DoReadMemory(const ProcessAddress &vm_addr, void *buf, size_t size,
                       Status &error) override;
   size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
                        Status &error) override;
diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp 
b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
index 1b3617c90e3e7..769bca2d1b819 100644
--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
+++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
@@ -524,8 +524,9 @@ Status ProcessElfCore::DoDestroy() { return Status(); }
 bool ProcessElfCore::IsAlive() { return true; }
 
 // Process Memory
-size_t ProcessElfCore::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
-                                  Status &error) {
+size_t ProcessElfCore::ReadMemory(const ProcessAddress &process_addr, void 
*buf,
+                                  size_t size, Status &error) {
+  lldb::addr_t addr = process_addr.GetValue();
   if (lldb::ABISP abi_sp = GetABI())
     addr = abi_sp->FixAnyAddress(addr);
 
@@ -562,8 +563,9 @@ Status ProcessElfCore::DoGetMemoryRegionInfo(lldb::addr_t 
load_addr,
   return Status();
 }
 
-size_t ProcessElfCore::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
-                                    Status &error) {
+size_t ProcessElfCore::DoReadMemory(const ProcessAddress &process_addr,
+                                    void *buf, size_t size, Status &error) {
+  lldb::addr_t addr = process_addr.GetValue();
   ObjectFile *core_objfile = m_core_module_sp->GetObjectFile();
 
   if (core_objfile == nullptr)
diff --git a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h 
b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
index 4233a80fa3ac7..42b6f5f3c6721 100644
--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
+++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
@@ -82,11 +82,11 @@ class ProcessElfCore : public 
lldb_private::PostMortemProcess {
   bool WarnBeforeDetach() const override { return false; }
 
   // Process Memory
-  size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
-                    lldb_private::Status &error) override;
+  size_t ReadMemory(const lldb_private::ProcessAddress &addr, void *buf,
+                    size_t size, lldb_private::Status &error) override;
 
-  size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
-                      lldb_private::Status &error) override;
+  size_t DoReadMemory(const lldb_private::ProcessAddress &addr, void *buf,
+                      size_t size, lldb_private::Status &error) override;
 
   // We do not implement DoReadMemoryTags. Instead all the work is done in
   // ReadMemoryTags which avoids having to unpack and repack tags.
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index bed8f936ec888..d8005ebfcea72 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -2905,10 +2905,11 @@ void ProcessGDBRemote::WillPublicStop() {
 }
 
 // Process Memory
-size_t ProcessGDBRemote::DoReadMemory(addr_t addr, void *buf, size_t size,
-                                      Status &error) {
+size_t ProcessGDBRemote::DoReadMemory(const ProcessAddress &process_addr,
+                                      void *buf, size_t size, Status &error) {
   using xPacketState = GDBRemoteCommunicationClient::xPacketState;
 
+  lldb::addr_t addr = process_addr.GetValue();
   GetMaxMemorySize();
   xPacketState x_state = m_gdb_comm.GetxPacketState();
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index 4b60f9c662910..f132858e14ca7 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -138,8 +138,8 @@ class ProcessGDBRemote : public Process,
   void WillPublicStop() override;
 
   // Process Memory
-  size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
-                      Status &error) override;
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override;
 
   /// Override of DoReadMemoryRanges that uses MultiMemRead to perform this
   /// operation in a single packet.
diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp 
b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
index 91a977bb69bdc..0be90167520d6 100644
--- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
+++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
@@ -721,15 +721,17 @@ bool ProcessMachCore::IsAlive() { return true; }
 bool ProcessMachCore::WarnBeforeDetach() const { return false; }
 
 // Process Memory
-size_t ProcessMachCore::ReadMemory(addr_t addr, void *buf, size_t size,
-                                   Status &error) {
+size_t ProcessMachCore::ReadMemory(const ProcessAddress &process_addr,
+                                   void *buf, size_t size, Status &error) {
+  lldb::addr_t addr = process_addr.GetValue();
   // Don't allow the caching that lldb_private::Process::ReadMemory does since
   // in core files we have it all cached our our core file anyway.
   return DoReadMemory(FixAnyAddress(addr), buf, size, error);
 }
 
-size_t ProcessMachCore::DoReadMemory(addr_t addr, void *buf, size_t size,
-                                     Status &error) {
+size_t ProcessMachCore::DoReadMemory(const ProcessAddress &process_addr,
+                                     void *buf, size_t size, Status &error) {
+  lldb::addr_t addr = process_addr.GetValue();
   ObjectFile *core_objfile = m_core_module_sp->GetObjectFile();
   size_t bytes_read = 0;
 
diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.h 
b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.h
index 6ba9f2354edf9..b425168c5c148 100644
--- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.h
+++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.h
@@ -62,11 +62,11 @@ class ProcessMachCore : public 
lldb_private::PostMortemProcess {
   bool WarnBeforeDetach() const override;
 
   // Process Memory
-  size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
-                    lldb_private::Status &error) override;
+  size_t ReadMemory(const lldb_private::ProcessAddress &addr, void *buf,
+                    size_t size, lldb_private::Status &error) override;
 
-  size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
-                      lldb_private::Status &error) override;
+  size_t DoReadMemory(const lldb_private::ProcessAddress &addr, void *buf,
+                      size_t size, lldb_private::Status &error) override;
 
   lldb::addr_t GetImageInfoAddress() override;
 
diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp 
b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
index 7b3c090286349..ca448434b81b8 100644
--- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
+++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.cpp
@@ -314,15 +314,17 @@ bool ProcessMinidump::IsAlive() { return true; }
 
 bool ProcessMinidump::WarnBeforeDetach() const { return false; }
 
-size_t ProcessMinidump::ReadMemory(lldb::addr_t addr, void *buf, size_t size,
-                                   Status &error) {
+size_t ProcessMinidump::ReadMemory(const ProcessAddress &process_addr,
+                                   void *buf, size_t size, Status &error) {
+  lldb::addr_t addr = process_addr.GetValue();
   // Don't allow the caching that lldb_private::Process::ReadMemory does since
   // we have it all cached in our dump file anyway.
   return DoReadMemory(addr, buf, size, error);
 }
 
-size_t ProcessMinidump::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
-                                     Status &error) {
+size_t ProcessMinidump::DoReadMemory(const ProcessAddress &process_addr,
+                                     void *buf, size_t size, Status &error) {
+  lldb::addr_t addr = process_addr.GetValue();
 
   llvm::Expected<llvm::ArrayRef<uint8_t>> mem_maybe =
       m_minidump_parser->GetMemory(addr, size);
diff --git a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h 
b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
index ad8d0ed7a4832..590af4d0426f7 100644
--- a/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
+++ b/lldb/source/Plugins/Process/minidump/ProcessMinidump.h
@@ -68,10 +68,10 @@ class ProcessMinidump : public PostMortemProcess {
 
   bool WarnBeforeDetach() const override;
 
-  size_t ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+  size_t ReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                     Status &error) override;
 
-  size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+  size_t DoReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                       Status &error) override;
 
   ArchSpec GetArchitecture();
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp 
b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
index 502c2f1146e7a..e95425e87374e 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.cpp
@@ -240,8 +240,9 @@ Status ScriptedProcess::DoDestroy() { return Status(); }
 
 bool ScriptedProcess::IsAlive() { return GetInterface().IsAlive(); }
 
-size_t ScriptedProcess::DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
-                                     Status &error) {
+size_t ScriptedProcess::DoReadMemory(const ProcessAddress &process_addr,
+                                     void *buf, size_t size, Status &error) {
+  lldb::addr_t addr = process_addr.GetValue();
   lldb::DataExtractorSP data_extractor_sp =
       GetInterface().ReadMemoryAtAddress(addr, size, error);
 
diff --git a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h 
b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
index 9510f2f06dabd..c3cdf30a9d477 100644
--- a/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
+++ b/lldb/source/Plugins/Process/scripted/ScriptedProcess.h
@@ -67,7 +67,7 @@ class ScriptedProcess : public Process {
 
   bool IsAlive() override;
 
-  size_t DoReadMemory(lldb::addr_t addr, void *buf, size_t size,
+  size_t DoReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                       Status &error) override;
 
   size_t DoWriteMemory(lldb::addr_t vm_addr, const void *buf, size_t size,
diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp 
b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
index f7d744c967ea0..bf454b2ca4476 100644
--- a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
@@ -127,8 +127,9 @@ size_t ProcessWasm::ReadGlobal(uint32_t module_id, uint32_t 
index, void *buf,
   return size;
 }
 
-size_t ProcessWasm::ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
-                               Status &error) {
+size_t ProcessWasm::ReadMemory(const ProcessAddress &process_addr, void *buf,
+                               size_t size, Status &error) {
+  lldb::addr_t vm_addr = process_addr.GetValue();
   wasm_addr_t wasm_addr(vm_addr);
 
   switch (wasm_addr.GetType()) {
diff --git a/lldb/source/Plugins/Process/wasm/ProcessWasm.h 
b/lldb/source/Plugins/Process/wasm/ProcessWasm.h
index 9bce07ec5691c..1e73e42af412a 100644
--- a/lldb/source/Plugins/Process/wasm/ProcessWasm.h
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.h
@@ -37,7 +37,7 @@ class ProcessWasm : public 
process_gdb_remote::ProcessGDBRemote {
 
   llvm::StringRef GetPluginName() override;
 
-  size_t ReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
+  size_t ReadMemory(const ProcessAddress &vm_addr, void *buf, size_t size,
                     Status &error) override;
 
   bool CanDebug(lldb::TargetSP target_sp,
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 256ce12abc1ef..1aeb3f0591f53 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -2035,7 +2035,9 @@ Status Process::DisableSoftwareBreakpoint(BreakpointSite 
*bp_site) {
 // code
 //#define VERIFY_MEMORY_READS
 
-size_t Process::ReadMemory(addr_t addr, void *buf, size_t size, Status &error) 
{
+size_t Process::ReadMemory(const ProcessAddress &process_addr, void *buf,
+                           size_t size, Status &error) {
+  lldb::addr_t addr = process_addr.GetValue();
   if (ABISP abi_sp = GetABI())
     addr = abi_sp->FixAnyAddress(addr);
 
diff --git a/lldb/source/Target/ProcessTrace.cpp 
b/lldb/source/Target/ProcessTrace.cpp
index 50000f22900f4..38816b6625d5c 100644
--- a/lldb/source/Target/ProcessTrace.cpp
+++ b/lldb/source/Target/ProcessTrace.cpp
@@ -93,8 +93,9 @@ void ProcessTrace::RefreshStateAfterStop() {}
 
 Status ProcessTrace::DoDestroy() { return Status(); }
 
-size_t ProcessTrace::ReadMemory(addr_t addr, void *buf, size_t size,
-                                Status &error) {
+size_t ProcessTrace::ReadMemory(const ProcessAddress &process_addr, void *buf,
+                                size_t size, Status &error) {
+  lldb::addr_t addr = process_addr.GetValue();
   if (const ABISP &abi = GetABI())
     addr = abi->FixAnyAddress(addr);
 
@@ -127,8 +128,9 @@ bool ProcessTrace::GetProcessInfo(ProcessInstanceInfo 
&info) {
   return true;
 }
 
-size_t ProcessTrace::DoReadMemory(addr_t addr, void *buf, size_t size,
-                                  Status &error) {
+size_t ProcessTrace::DoReadMemory(const ProcessAddress &process_addr, void 
*buf,
+                                  size_t size, Status &error) {
+  lldb::addr_t addr = process_addr.GetValue();
   Address resolved_address;
   GetTarget().ResolveLoadAddress(addr, resolved_address);
 
diff --git 
a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp 
b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
index 174ab1a0143d4..8ee3510e67e0b 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
@@ -59,8 +59,9 @@ Status ProcessMockAccelerator::Signal(int signo) {
 
 Status ProcessMockAccelerator::Kill() { return Status(); }
 
-Status ProcessMockAccelerator::ReadMemory(lldb::addr_t addr, void *buf,
-                                          size_t size, size_t &bytes_read) {
+Status ProcessMockAccelerator::ReadMemory(const ProcessAddress &process_addr,
+                                          void *buf, size_t size,
+                                          size_t &bytes_read) {
   bytes_read = 0;
   return Status::FromErrorString("unimplemented");
 }
diff --git 
a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h 
b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
index 6fc07b5ac011a..6346773e40141 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
@@ -40,7 +40,7 @@ class ProcessMockAccelerator : public NativeProcessProtocol {
   Status Signal(int signo) override;
   Status Kill() override;
 
-  Status ReadMemory(lldb::addr_t addr, void *buf, size_t size,
+  Status ReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                     size_t &bytes_read) override;
   Status WriteMemory(lldb::addr_t addr, const void *buf, size_t size,
                      size_t &bytes_written) override;
diff --git a/lldb/unittests/DataFormatter/FormatterSectionTest.cpp 
b/lldb/unittests/DataFormatter/FormatterSectionTest.cpp
index 35a24bef7b0aa..a67bb7c44ab1a 100644
--- a/lldb/unittests/DataFormatter/FormatterSectionTest.cpp
+++ b/lldb/unittests/DataFormatter/FormatterSectionTest.cpp
@@ -108,8 +108,8 @@ struct MockProcess : Process {
     return false;
   };
 
-  size_t DoReadMemory(addr_t vm_addr, void *buf, size_t size,
-                      Status &error) override {
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override {
     return 0;
   }
 };
diff --git a/lldb/unittests/Expression/DWARFExpressionTest.cpp 
b/lldb/unittests/Expression/DWARFExpressionTest.cpp
index e8bca7208c8d5..74a1b66b9a587 100644
--- a/lldb/unittests/Expression/DWARFExpressionTest.cpp
+++ b/lldb/unittests/Expression/DWARFExpressionTest.cpp
@@ -142,8 +142,9 @@ struct MockProcess : Process {
   MockProcess(lldb::TargetSP target_sp, lldb::ListenerSP listener_sp,
               MockMemory memory)
       : Process(target_sp, listener_sp), m_memory(std::move(memory)) {}
-  size_t DoReadMemory(addr_t vm_addr, void *buf, size_t size,
-                      Status &error) override {
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override {
+    addr_t vm_addr = process_addr.GetValue();
     auto expected_memory = m_memory.ReadMemory(vm_addr, size);
     if (!expected_memory) {
       error = Status::FromError(expected_memory.takeError());
@@ -153,9 +154,9 @@ struct MockProcess : Process {
     std::memcpy(buf, expected_memory->data(), expected_memory->size());
     return size;
   }
-  size_t ReadMemory(addr_t addr, void *buf, size_t size,
+  size_t ReadMemory(const ProcessAddress &process_addr, void *buf, size_t size,
                     Status &status) override {
-    return DoReadMemory(addr, buf, size, status);
+    return DoReadMemory(process_addr, buf, size, status);
   }
   bool CanDebug(lldb::TargetSP, bool) override { return true; }
   Status DoDestroy() override { return Status(); }
diff --git a/lldb/unittests/Expression/IRMemoryMapTest.cpp 
b/lldb/unittests/Expression/IRMemoryMapTest.cpp
index 7c78a3abd359c..df9a3862efc77 100644
--- a/lldb/unittests/Expression/IRMemoryMapTest.cpp
+++ b/lldb/unittests/Expression/IRMemoryMapTest.cpp
@@ -36,8 +36,8 @@ class NoJITProcess : public Process {
   }
   Status DoDestroy() override { return {}; }
   void RefreshStateAfterStop() override {}
-  size_t DoReadMemory(addr_t vm_addr, void *buf, size_t size,
-                      Status &error) override {
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override {
     return 0;
   }
   bool DoUpdateThreadList(ThreadList &old_thread_list,
diff --git a/lldb/unittests/Process/ProcessEventDataTest.cpp 
b/lldb/unittests/Process/ProcessEventDataTest.cpp
index 88ea394bbb1e5..237ee639eda16 100644
--- a/lldb/unittests/Process/ProcessEventDataTest.cpp
+++ b/lldb/unittests/Process/ProcessEventDataTest.cpp
@@ -49,8 +49,8 @@ class DummyProcess : public Process {
   }
   Status DoDestroy() override { return {}; }
   void RefreshStateAfterStop() override {}
-  size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
-                      Status &error) override {
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override {
     return 0;
   }
   bool DoUpdateThreadList(ThreadList &old_thread_list,
diff --git a/lldb/unittests/Process/elf-core/ThreadElfCoreTest.cpp 
b/lldb/unittests/Process/elf-core/ThreadElfCoreTest.cpp
index 68919945198d4..95c8fa24702f4 100644
--- a/lldb/unittests/Process/elf-core/ThreadElfCoreTest.cpp
+++ b/lldb/unittests/Process/elf-core/ThreadElfCoreTest.cpp
@@ -59,8 +59,8 @@ struct DummyProcess : public Process {
   }
   Status DoDestroy() override { return {}; }
   void RefreshStateAfterStop() override {}
-  size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
-                      Status &error) override {
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override {
     return 0;
   }
   bool DoUpdateThreadList(ThreadList &old_thread_list,
diff --git a/lldb/unittests/Target/ExecutionContextTest.cpp 
b/lldb/unittests/Target/ExecutionContextTest.cpp
index 7918b252ebe35..6415bce7c18eb 100644
--- a/lldb/unittests/Target/ExecutionContextTest.cpp
+++ b/lldb/unittests/Target/ExecutionContextTest.cpp
@@ -51,8 +51,8 @@ class DummyProcess : public Process {
   }
   Status DoDestroy() override { return {}; }
   void RefreshStateAfterStop() override {}
-  size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
-                      Status &error) override {
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override {
     return 0;
   }
   bool DoUpdateThreadList(ThreadList &old_thread_list,
diff --git a/lldb/unittests/Target/LocateModuleCallbackTest.cpp 
b/lldb/unittests/Target/LocateModuleCallbackTest.cpp
index 1fe4487892060..fbf716c017f19 100644
--- a/lldb/unittests/Target/LocateModuleCallbackTest.cpp
+++ b/lldb/unittests/Target/LocateModuleCallbackTest.cpp
@@ -64,8 +64,8 @@ class MockProcess : public Process {
     return false;
   }
 
-  size_t DoReadMemory(addr_t vm_addr, void *buf, size_t size,
-                      Status &error) override {
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override {
     return 0;
   }
 
diff --git a/lldb/unittests/Target/MemoryTest.cpp 
b/lldb/unittests/Target/MemoryTest.cpp
index f9d3dc1fee72f..5c8d1a0426803 100644
--- a/lldb/unittests/Target/MemoryTest.cpp
+++ b/lldb/unittests/Target/MemoryTest.cpp
@@ -97,8 +97,8 @@ class DummyProcess : public Process {
   void RefreshStateAfterStop() override {}
   // Required by Target::ReadMemory() to call Process::ReadMemory()
   bool IsAlive() override { return true; }
-  size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
-                      Status &error) override {
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override {
     if (m_bytes_left == 0)
       return 0;
 
@@ -469,8 +469,9 @@ class DummyReaderProcess : public Process {
   bool read_less_than_requested = false;
   bool read_more_than_requested = false;
 
-  size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
-                      Status &error) override {
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override {
+    lldb::addr_t vm_addr = process_addr.GetValue();
     if (read_less_than_requested && size > 0)
       size--;
     if (read_more_than_requested)
@@ -635,8 +636,9 @@ class StringReaderProcess : public Process {
     strcpy(&memory[300], long_str.data());
   }
 
-  size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
-                      Status &error) override {
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override {
+    lldb::addr_t vm_addr = process_addr.GetValue();
     if (vm_addr >= 1024) {
       error = Status::FromErrorString("out of bounds!");
       return 0;
@@ -800,7 +802,8 @@ class DummyMSBReaderProcess : public Process {
   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 {
+  size_t DoReadMemory(const ProcessAddress &, void *, size_t,
+                      Status &) override {
     llvm_unreachable("don't call this");
   }
 };
diff --git a/lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h 
b/lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h
index 1a017122411a8..087a6e7f92913 100644
--- a/lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h
+++ b/lldb/unittests/TestingSupport/Host/NativeProcessTestUtils.h
@@ -70,8 +70,9 @@ template <typename T> class MockProcess : public T {
 
   // Redirect base class Read/Write Memory methods to functions whose 
signatures
   // are more mock-friendly.
-  Status ReadMemory(addr_t Addr, void *Buf, size_t Size,
+  Status ReadMemory(const ProcessAddress &process_addr, void *Buf, size_t Size,
                     size_t &BytesRead) /*override*/ {
+    addr_t Addr = process_addr.GetValue();
     auto ExpectedMemory = this->ReadMemory(Addr, Size);
     if (!ExpectedMemory) {
       BytesRead = 0;
diff --git a/lldb/unittests/Thread/ThreadTest.cpp 
b/lldb/unittests/Thread/ThreadTest.cpp
index 4f4a5558db8df..ee23ce0c172ee 100644
--- a/lldb/unittests/Thread/ThreadTest.cpp
+++ b/lldb/unittests/Thread/ThreadTest.cpp
@@ -75,8 +75,8 @@ class DummyProcess : public Process {
   }
   Status DoDestroy() override { return {}; }
   void RefreshStateAfterStop() override {}
-  size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
-                      Status &error) override {
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override {
     return 0;
   }
   bool DoUpdateThreadList(ThreadList &old_thread_list,
diff --git a/lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp 
b/lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp
index c3fb8cbeb60ae..955dae59281e6 100644
--- a/lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp
+++ b/lldb/unittests/ValueObject/DumpValueObjectOptionsTests.cpp
@@ -42,8 +42,8 @@ struct MockProcess : Process {
     return false;
   };
 
-  size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
-                      Status &error) override {
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override {
     // No need to read memory in these tests.
     return size;
   }
diff --git a/lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp 
b/lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp
index 0f3d2d2ba9d68..058353806d2ac 100644
--- a/lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp
+++ b/lldb/unittests/ValueObject/DynamicValueObjectLocalBuffer.cpp
@@ -142,8 +142,8 @@ struct MockProcess : Process {
     return false;
   };
 
-  size_t DoReadMemory(lldb::addr_t vm_addr, void *buf, size_t size,
-                      Status &error) override {
+  size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
+                      size_t size, Status &error) override {
     // No need to read memory in these tests.
     return size;
   }

>From 692b598c5b01119677269fbcb04226e8da80849f Mon Sep 17 00:00:00 2001
From: satya janga <[email protected]>
Date: Tue, 4 Aug 2026 12:28:23 -0700
Subject: [PATCH 2/2] [lldb] Add generic address space support

Add a generic mechanism for a process to report the address spaces it
exposes and to read memory from a specific one. Most processes have a
single flat address space, but some (such as GPUs) have several, where
the same numeric address refers to different storage depending on the
address space (for example global, local, private or generic memory).

Everything lives in the generic layers; there is no plugin specific code.

- A new "jAddressSpacesInfo" packet returns the address spaces of the
  process as JSON. NativeProcessProtocol gains a virtual GetAddressSpaces()
  that defaults to empty, and the server only advertises the feature when
  a plugin provides one. Process resolves the list once, on demand.

- Address spaces are carried on the existing memory packets with an
  optional ";address_space:<id>;" suffix, negotiated with the
  "address-spaces+" qSupported feature. The suffix is only sent for a
  non-default address space, so address space unaware stubs and reads are
  unaffected.

- Process::ReadMemory(const ProcessAddress &) reads through the memory
  cache for the default address space, and otherwise validates the
  address space id and goes straight to the process plugin.

- Public API: SBProcessAddress, SBProcess::GetAddressSpaceID() and an
  SBProcess::ReadMemory() overload taking an SBProcessAddress.

Documented in docs/resources/lldbgdbremote.md.

Tests: GDBRemoteCommunicationClient round trip tests for the qSupported
negotiation and jAddressSpacesInfo (supported, unsupported, malformed);
an end to end MockGDBServer test that connects to a target exposing two
address spaces and verifies the same address reads back different bytes
from each; and lldb-server tests for the default server responses.
---
 lldb/bindings/python/python-typemaps.swig     |  6 ++
 lldb/docs/resources/lldbgdbremote.md          | 72 ++++++++++++++++++
 lldb/include/lldb/API/SBAddress.h             | 26 +++++++
 lldb/include/lldb/API/SBDefines.h             |  1 +
 lldb/include/lldb/API/SBProcess.h             |  7 ++
 .../lldb/Host/common/NativeProcessProtocol.h  |  8 +-
 lldb/include/lldb/Target/Process.h            | 21 ++++++
 .../lldb/Utility/StringExtractorGDBRemote.h   |  1 +
 .../tools/lldb-server/gdbremote_testcase.py   |  1 +
 lldb/source/API/SBAddress.cpp                 | 32 ++++++++
 lldb/source/API/SBProcess.cpp                 | 51 +++++++++++++
 .../GDBRemoteCommunicationClient.cpp          | 32 ++++++++
 .../gdb-remote/GDBRemoteCommunicationClient.h |  9 +++
 .../GDBRemoteCommunicationServerLLGS.cpp      | 51 +++++++++++--
 .../GDBRemoteCommunicationServerLLGS.h        |  3 +
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 27 ++++++-
 .../Process/gdb-remote/ProcessGDBRemote.h     |  2 +
 lldb/source/Target/Process.cpp                | 66 +++++++++++++++-
 .../Utility/StringExtractorGDBRemote.cpp      |  2 +
 .../TestAddressSpaceMemoryRead.py             | 75 +++++++++++++++++++
 .../lldb-server/TestGdbRemoteAddressSpaces.py | 28 +++++++
 .../GDBRemoteCommunicationClientTest.cpp      | 50 +++++++++++++
 22 files changed, 560 insertions(+), 11 deletions(-)
 create mode 100644 
lldb/test/API/functionalities/gdb_remote_client/TestAddressSpaceMemoryRead.py
 create mode 100644 
lldb/test/API/tools/lldb-server/TestGdbRemoteAddressSpaces.py

diff --git a/lldb/bindings/python/python-typemaps.swig 
b/lldb/bindings/python/python-typemaps.swig
index d148bc27ad413..df1ffe190c37e 100644
--- a/lldb/bindings/python/python-typemaps.swig
+++ b/lldb/bindings/python/python-typemaps.swig
@@ -343,6 +343,12 @@ AND call SWIG_fail at the same time, because it will 
result in a double free.
   $1 = (void *)malloc($2);
 }
 
+// typecheck so overloaded methods taking a buffer (e.g. SBProcess::ReadMemory)
+// can participate in overload resolution.
+%typemap(typecheck, precedence=SWIG_TYPECHECK_INTEGER) (void *buf, size_t 
size) {
+  $1 = PyLong_Check($input) ? 1 : 0;
+}
+
 // Return the buffer.  Discarding any previous return result
 // See also SBProcess::ReadMemory.
 %typemap(argout) (void *buf, size_t size) {
diff --git a/lldb/docs/resources/lldbgdbremote.md 
b/lldb/docs/resources/lldbgdbremote.md
index 93090c19c5ec0..57b5fa91c6639 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -822,6 +822,74 @@ This is a performance optimization, which speeds up 
debugging by avoiding
 multiple round-trips for retrieving thread information. The information from 
this
 packet can be retrieved using a combination of `qThreadStopInfo` and `m` 
packets.
 
+## jAddressSpacesInfo
+
+Ask the server for the address spaces the process exposes.
+
+Most processes have a single, flat address space, but some (such as GPUs) have
+multiple address spaces where the same numeric address refers to different
+storage depending on the address space (for example global, local, private or
+generic memory). This packet lets the client discover those address spaces.
+
+This packet requires the `address-spaces+` feature from `qSupported`, which a
+server only advertises when its process exposes address spaces.
+
+The response is a JSON array of dictionaries, one per address space:
+```
+    [
+      {"name":"global","space_id":1,"is_thread_specific":false},
+      {"name":"local","space_id":2,"is_thread_specific":true}
+    ]
+```
+
+Each dictionary has the following keys:
+
+* `name`: the human readable name of the address space.
+* `space_id`: the integer identifier of the address space.
+* `is_thread_specific`: true if the address space is thread specific.
+
+If a server that advertised `address-spaces+` has no address spaces to report,
+it replies with an unsupported (empty) response.
+
+**Priority To Implement:** Low
+
+Only needed for targets that expose more than one address space.
+
+## address-spaces (qSupported feature)
+
+A server advertises `address-spaces+` in its `qSupported` response when its
+process exposes address spaces. This single feature implies both the
+`jAddressSpacesInfo` packet and the optional `;address_space:<id>;` suffix on 
the
+memory packets described below.
+
+### The `address_space` suffix
+
+To read from a specific address space, the client appends an optional
+`;address_space:<id>;` key-value suffix to the existing memory packets rather
+than introducing a dedicated packet, where `<id>` is the address space id
+reported by `jAddressSpacesInfo` as a big-endian base 16 value, matching the
+`;thread:<tid>;` suffix. An id of `0`, or the absence of the suffix, means the
+default address space and behaves exactly as before, so address-space-unaware
+stubs and reads are unaffected.
+
+```
+send packet: $x1000,4;address_space:10;
+read packet: $<binary encoding of the 4 bytes at 0x1000 in address space 16>
+```
+
+Packets that currently accept the suffix:
+
+* `m` / `x`: read memory from a specific address space.
+
+Because the suffix is an optional key-value pair on the existing packets, the
+same mechanism can be extended to other address-bearing packets (memory writes
+`M` / `X`, breakpoints `z` / `Z`, etc.) as the need arises, without introducing
+new packets or bifurcating the address-space-aware and unaware code paths.
+
+**Priority To Implement:** Low
+
+Only needed for targets that expose more than one address space.
+
 ## MultiMemRead
 
 Read memory from multiple memory ranges.
@@ -2717,6 +2785,10 @@ xADDRESS,LENGTH
 
 where both `ADDRESS` and `LENGTH` are big-endian base 16 values.
 
+The `x` packet may also carry an optional `;address_space:<id>;` suffix to read
+from a non-default address space; see
+[the address-spaces feature](#address-spaces-qsupported-feature).
+
 To test if this packet is available, send a addr/len of 0:
 ```
 x0,0
diff --git a/lldb/include/lldb/API/SBAddress.h 
b/lldb/include/lldb/API/SBAddress.h
index 430dad4862dbf..c459e8963ef79 100644
--- a/lldb/include/lldb/API/SBAddress.h
+++ b/lldb/include/lldb/API/SBAddress.h
@@ -130,6 +130,32 @@ class LLDB_API SBAddress {
 bool LLDB_API operator==(const SBAddress &lhs, const SBAddress &rhs);
 #endif
 
+/// A memory address, optionally in a non-default address space.
+class LLDB_API SBProcessAddress {
+public:
+  SBProcessAddress(const SBProcessAddress &rhs);
+
+  /// A load address in the default address space.
+  SBProcessAddress(lldb::addr_t load_addr);
+
+  /// An address in the address space with the given id (0 = default).
+  SBProcessAddress(lldb::addr_t addr, lldb::addr_space_t address_space_id);
+
+  ~SBProcessAddress();
+
+  const lldb::SBProcessAddress &operator=(const lldb::SBProcessAddress &rhs);
+
+protected:
+  friend class SBProcess;
+
+  lldb_private::ProcessAddress &ref();
+
+  const lldb_private::ProcessAddress &ref() const;
+
+private:
+  std::unique_ptr<lldb_private::ProcessAddress> m_opaque_up;
+};
+
 } // namespace lldb
 
 #endif // LLDB_API_SBADDRESS_H
diff --git a/lldb/include/lldb/API/SBDefines.h 
b/lldb/include/lldb/API/SBDefines.h
index 7ec8e56067aa6..951a656c05016 100644
--- a/lldb/include/lldb/API/SBDefines.h
+++ b/lldb/include/lldb/API/SBDefines.h
@@ -45,6 +45,7 @@ namespace lldb {
 class LLDB_API SBAddress;
 class LLDB_API SBAddressRange;
 class LLDB_API SBAddressRangeList;
+class LLDB_API SBProcessAddress;
 class LLDB_API SBAttachInfo;
 class LLDB_API SBBlock;
 class LLDB_API SBBreakpoint;
diff --git a/lldb/include/lldb/API/SBProcess.h 
b/lldb/include/lldb/API/SBProcess.h
index f42b30007a64b..58e77434c4dec 100644
--- a/lldb/include/lldb/API/SBProcess.h
+++ b/lldb/include/lldb/API/SBProcess.h
@@ -199,6 +199,13 @@ class LLDB_API SBProcess {
 
   size_t ReadMemory(addr_t addr, void *buf, size_t size, lldb::SBError &error);
 
+  /// Read memory that may be in a non-default address space.
+  size_t ReadMemory(SBProcessAddress process_addr, void *buf, size_t size,
+                    lldb::SBError &error);
+
+  /// Resolve an address space name to its id, or 
LLDB_INVALID_ADDRESS_SPACE_ID.
+  lldb::addr_space_t GetAddressSpaceID(const char *name, lldb::SBError &error);
+
   size_t WriteMemory(addr_t addr, const void *buf, size_t size,
                      lldb::SBError &error);
 
diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h 
b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 67206c4b55b79..afe62e2a52497 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -14,6 +14,7 @@
 #include "NativeWatchpointList.h"
 #include "lldb/Host/Host.h"
 #include "lldb/Host/MainLoop.h"
+#include "lldb/Utility/AddressSpace.h"
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/Iterable.h"
 #include "lldb/Utility/ProcessAddress.h"
@@ -97,6 +98,10 @@ class NativeProcessProtocol {
   virtual Status GetMemoryRegionInfo(lldb::addr_t load_addr,
                                      MemoryRegionInfo &range_info);
 
+  /// Served over the "jAddressSpacesInfo" packet.
+  virtual std::vector<AddressSpaceInfo> GetAddressSpaces() { return {}; }
+
+  /// Plugins without address spaces should error on a non-default one.
   virtual Status ReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                             size_t &bytes_read) = 0;
 
@@ -298,8 +303,9 @@ class NativeProcessProtocol {
     siginfo_read = (1u << 8),
     libraries = (1u << 9),
     accelerator_plugins = (1u << 10),
+    address_spaces = (1u << 11),
 
-    LLVM_MARK_AS_BITMASK_ENUM(accelerator_plugins)
+    LLVM_MARK_AS_BITMASK_ENUM(address_spaces)
   };
 
   class Manager {
diff --git a/lldb/include/lldb/Target/Process.h 
b/lldb/include/lldb/Target/Process.h
index f4902ceed1b8a..bf6ea6f9395bd 100644
--- a/lldb/include/lldb/Target/Process.h
+++ b/lldb/include/lldb/Target/Process.h
@@ -46,6 +46,7 @@
 #include "lldb/Target/ThreadList.h"
 #include "lldb/Target/ThreadPlanStack.h"
 #include "lldb/Target/Trace.h"
+#include "lldb/Utility/AddressSpace.h"
 #include "lldb/Utility/AddressableBits.h"
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/Args.h"
@@ -2059,6 +2060,17 @@ class Process : public 
std::enable_shared_from_this<Process>,
   virtual Status
   GetMemoryRegions(lldb_private::MemoryRegionInfos &region_list);
 
+  /// Empty for single address space processes.
+  llvm::ArrayRef<AddressSpaceInfo> GetAddressSpaces() const {
+    return m_address_spaces;
+  }
+
+  llvm::Expected<AddressSpaceInfo>
+  GetAddressSpaceInfo(llvm::StringRef address_space_name);
+
+  llvm::Expected<AddressSpaceInfo>
+  GetAddressSpaceInfo(lldb::addr_space_t address_space_id);
+
   /// Get the number of watchpoints supported by this target.
   ///
   /// We may be able to determine the number of watchpoints available
@@ -3055,6 +3067,12 @@ void PruneThreadPlans();
   virtual size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
                               size_t size, Status &error) = 0;
 
+  /// Populate m_address_spaces from the process plugin.
+  virtual void DoResolveAddressSpaces() {}
+
+  /// Calls DoResolveAddressSpaces() at most once.
+  void ResolveAddressSpaces();
+
   /// Reads each range individually via ReadMemoryFromInferior, bypassing the
   /// memory cache. Subclasses may override it to batch the reads more
   /// efficiently.
@@ -3517,6 +3535,9 @@ void PruneThreadPlans();
   ThreadList
       m_extended_thread_list; ///< Constituent for extended threads that may be
                               /// generated, cleared on natural stops
+  std::vector<AddressSpaceInfo>
+      m_address_spaces; ///< Empty for single address space processes.
+  llvm::once_flag m_address_spaces_resolved;
   lldb::RunDirection m_base_direction; ///< ThreadPlanBase run direction
   uint32_t m_extended_thread_stop_id; ///< The natural stop id when
                                       ///extended_thread_list was last updated
diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h 
b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
index 624a2febe857e..a16dfcfe1601a 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -108,6 +108,7 @@ class StringExtractorGDBRemote : public StringExtractor {
     eServerPacketType_QThreadSuffixSupported,
 
     eServerPacketType_jThreadsInfo,
+    eServerPacketType_jAddressSpacesInfo,
     eServerPacketType_qsThreadInfo,
     eServerPacketType_qfThreadInfo,
     eServerPacketType_qGetPid,
diff --git 
a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py 
b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
index ede6ea8490951..03ab78978c1e5 100644
--- 
a/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
+++ 
b/lldb/packages/Python/lldbsuite/test/tools/lldb-server/gdbremote_testcase.py
@@ -932,6 +932,7 @@ def add_qSupported_packets(self, client_features=[]):
         "PacketSize",
         "QStartNoAckMode",
         "QThreadSuffixSupported",
+        "address-spaces",
         "QListThreadsInStopReply",
         "qXfer:auxv:read",
         "qXfer:libraries:read",
diff --git a/lldb/source/API/SBAddress.cpp b/lldb/source/API/SBAddress.cpp
index 78acc2e34564d..f5571ea539ea3 100644
--- a/lldb/source/API/SBAddress.cpp
+++ b/lldb/source/API/SBAddress.cpp
@@ -11,9 +11,11 @@
 #include "lldb/API/SBProcess.h"
 #include "lldb/API/SBSection.h"
 #include "lldb/API/SBStream.h"
+#include "lldb/API/SBThread.h"
 #include "lldb/Core/Address.h"
 #include "lldb/Core/Module.h"
 #include "lldb/Symbol/LineEntry.h"
+#include "lldb/Target/Process.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Utility/Instrumentation.h"
 #include "lldb/Utility/StreamString.h"
@@ -260,3 +262,33 @@ SBLineEntry SBAddress::GetLineEntry() {
   }
   return sb_line_entry;
 }
+
+SBProcessAddress::SBProcessAddress(const SBProcessAddress &rhs)
+    : m_opaque_up(new ProcessAddress(rhs.ref())) {
+  LLDB_INSTRUMENT_VA(this, rhs);
+}
+
+SBProcessAddress::~SBProcessAddress() = default;
+
+SBProcessAddress::SBProcessAddress(lldb::addr_t load_addr)
+    : m_opaque_up(new ProcessAddress(load_addr)) {
+  LLDB_INSTRUMENT_VA(this);
+}
+
+SBProcessAddress::SBProcessAddress(lldb::addr_t addr,
+                                   lldb::addr_space_t address_space_id)
+    : m_opaque_up(new ProcessAddress(addr, address_space_id)) {
+  LLDB_INSTRUMENT_VA(this, addr, address_space_id);
+}
+
+ProcessAddress &SBProcessAddress::ref() { return *m_opaque_up; }
+
+const ProcessAddress &SBProcessAddress::ref() const { return *m_opaque_up; }
+
+const SBProcessAddress &
+SBProcessAddress::operator=(const SBProcessAddress &rhs) {
+  LLDB_INSTRUMENT_VA(this, rhs);
+  if (this != &rhs)
+    m_opaque_up = clone(rhs.m_opaque_up);
+  return *this;
+}
diff --git a/lldb/source/API/SBProcess.cpp b/lldb/source/API/SBProcess.cpp
index 08e39f754cf85..1467f132263a9 100644
--- a/lldb/source/API/SBProcess.cpp
+++ b/lldb/source/API/SBProcess.cpp
@@ -27,12 +27,14 @@
 #include "lldb/Target/SystemRuntime.h"
 #include "lldb/Target/Target.h"
 #include "lldb/Target/Thread.h"
+#include "lldb/Utility/AddressSpace.h"
 #include "lldb/Utility/Args.h"
 #include "lldb/Utility/LLDBLog.h"
 #include "lldb/Utility/ProcessInfo.h"
 #include "lldb/Utility/State.h"
 #include "lldb/Utility/Stream.h"
 
+#include "lldb/API/SBAddress.h"
 #include "lldb/API/SBBroadcaster.h"
 #include "lldb/API/SBCommandReturnObject.h"
 #include "lldb/API/SBDebugger.h"
@@ -906,6 +908,55 @@ size_t SBProcess::ReadMemory(addr_t addr, void *dst, 
size_t dst_len,
   return bytes_read;
 }
 
+size_t SBProcess::ReadMemory(SBProcessAddress process_addr, void *dst,
+                             size_t dst_len, SBError &sb_error) {
+  LLDB_INSTRUMENT_VA(this, process_addr, dst, dst_len, sb_error);
+
+  if (!dst) {
+    sb_error = Status::FromErrorStringWithFormat(
+        "no buffer provided to read %zu bytes into", dst_len);
+    return 0;
+  }
+
+  ProcessSP process_sp(GetSP());
+  if (!process_sp) {
+    sb_error = Status::FromErrorString("SBProcess is invalid");
+    return 0;
+  }
+
+  Process::StopLocker stop_locker;
+  if (!stop_locker.TryLock(&process_sp->GetRunLock())) {
+    sb_error = Status::FromErrorString("process is running");
+    return 0;
+  }
+
+  std::lock_guard<std::recursive_mutex> guard(
+      process_sp->GetTarget().GetAPIMutex());
+  return process_sp->ReadMemory(process_addr.ref(), dst, dst_len,
+                                sb_error.ref());
+}
+
+lldb::addr_space_t SBProcess::GetAddressSpaceID(const char *name,
+                                                SBError &sb_error) {
+  LLDB_INSTRUMENT_VA(this, name, sb_error);
+
+  ProcessSP process_sp(GetSP());
+  if (!process_sp) {
+    sb_error = Status::FromErrorString("SBProcess is invalid");
+    return LLDB_INVALID_ADDRESS_SPACE_ID;
+  }
+
+  std::lock_guard<std::recursive_mutex> guard(
+      process_sp->GetTarget().GetAPIMutex());
+  llvm::Expected<AddressSpaceInfo> info =
+      process_sp->GetAddressSpaceInfo(name ? name : "");
+  if (!info) {
+    sb_error = Status::FromError(info.takeError());
+    return LLDB_INVALID_ADDRESS_SPACE_ID;
+  }
+  return info->space_id;
+}
+
 size_t SBProcess::ReadCStringFromMemory(addr_t addr, void *buf, size_t size,
                                         lldb::SBError &sb_error) {
   LLDB_INSTRUMENT_VA(this, addr, buf, size, sb_error);
diff --git 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index b440869f25984..279f0510bfd31 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -386,6 +386,7 @@ void 
GDBRemoteCommunicationClient::ResetDiscoverableSettings(bool did_exec) {
     m_attach_or_wait_reply = eLazyBoolCalculate;
     m_avoid_g_packets = eLazyBoolCalculate;
     m_supports_multiprocess = eLazyBoolCalculate;
+    m_supports_address_spaces = false;
     m_supports_qSaveCore = eLazyBoolCalculate;
     m_supports_qXfer_auxv_read = eLazyBoolCalculate;
     m_supports_qXfer_libraries_read = eLazyBoolCalculate;
@@ -453,6 +454,7 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
   m_supports_QPassSignals = eLazyBoolNo;
   m_supports_memory_tagging = eLazyBoolNo;
   m_supports_qSaveCore = eLazyBoolNo;
+  m_supports_address_spaces = false;
   m_uses_native_signals = eLazyBoolNo;
   m_x_packet_state.reset();
   m_supports_reverse_continue = eLazyBoolNo;
@@ -513,6 +515,8 @@ void GDBRemoteCommunicationClient::GetRemoteQSupported() {
         m_supports_memory_tagging = eLazyBoolYes;
       else if (x == "qSaveCore+")
         m_supports_qSaveCore = eLazyBoolYes;
+      else if (x == "address-spaces+")
+        m_supports_address_spaces = true;
       else if (x == "native-signals+")
         m_uses_native_signals = eLazyBoolYes;
       else if (x == "binary-upload+")
@@ -1162,6 +1166,34 @@ 
GDBRemoteCommunicationClient::GetProcessStandaloneBinaries() {
   return m_binary_addresses;
 }
 
+std::vector<AddressSpaceInfo> GDBRemoteCommunicationClient::GetAddressSpaces() 
{
+  if (!m_supports_address_spaces)
+    return {};
+
+  StringExtractorGDBRemote response;
+  response.SetResponseValidatorToJSON();
+  if (SendPacketAndWaitForResponse("jAddressSpacesInfo", response) !=
+      PacketResult::Success)
+    return {};
+
+  if (response.IsUnsupportedResponse() || response.IsErrorResponse()) {
+    m_supports_address_spaces = false;
+    return {};
+  }
+
+  llvm::Expected<std::vector<AddressSpaceInfo>> info =
+      llvm::json::parse<std::vector<AddressSpaceInfo>>(response.Peek(),
+                                                       "AddressSpaceInfo");
+  if (info)
+    return std::move(*info);
+
+  Log *log = GetLog(GDBRLog::Process);
+  LLDB_LOG_ERROR(log, info.takeError(),
+                 "malformed jAddressSpacesInfo response '{1}': {0}",
+                 response.GetStringRef());
+  return {};
+}
+
 bool GDBRemoteCommunicationClient::GetGDBServerVersion() {
   if (m_qGDBServerVersion_is_valid == eLazyBoolCalculate) {
     m_gdb_server_name.clear();
diff --git 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 3a0a34f840c21..c64f85f6696be 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -20,6 +20,7 @@
 
 #include "lldb/Host/File.h"
 #include "lldb/Utility/AcceleratorGDBRemotePackets.h"
+#include "lldb/Utility/AddressSpace.h"
 #include "lldb/Utility/AddressableBits.h"
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/GDBRemote.h"
@@ -34,6 +35,7 @@
 #include "llvm/Support/VersionTuple.h"
 
 namespace lldb_private {
+
 namespace process_gdb_remote {
 
 /// The offsets used by the target when relocating the executable. Decoded from
@@ -223,6 +225,12 @@ class GDBRemoteCommunicationClient : public 
GDBRemoteClientBase {
 
   std::vector<lldb::addr_t> GetProcessStandaloneBinaries();
 
+  /// Empty if the server does not support "jAddressSpacesInfo".
+  std::vector<AddressSpaceInfo> GetAddressSpaces();
+
+  /// Whether the server advertised address-space support ("address-spaces+").
+  bool GetAddressSpacesSupported() { return m_supports_address_spaces; }
+
   void GetRemoteQSupported();
 
   bool GetVContSupported(llvm::StringRef flavor);
@@ -606,6 +614,7 @@ class GDBRemoteCommunicationClient : public 
GDBRemoteClientBase {
   LazyBool m_supports_error_string_reply = eLazyBoolCalculate;
   LazyBool m_supports_multiprocess = eLazyBoolCalculate;
   LazyBool m_supports_memory_tagging = eLazyBoolCalculate;
+  bool m_supports_address_spaces = false;
   LazyBool m_supports_qSaveCore = eLazyBoolCalculate;
   LazyBool m_uses_native_signals = eLazyBoolCalculate;
   std::optional<xPacketState> m_x_packet_state;
diff --git 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 2e824282079b4..289cde2c7ddd1 100644
--- 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -164,6 +164,9 @@ void 
GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_jThreadsInfo,
       &GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo);
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_jAddressSpacesInfo,
+      &GDBRemoteCommunicationServerLLGS::Handle_jAddressSpacesInfo);
   RegisterMemberFunctionHandler(
       StringExtractorGDBRemote::eServerPacketType_qWatchpointSupportInfo,
       &GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo);
@@ -2671,6 +2674,17 @@ GDBRemoteCommunicationServerLLGS::Handle_memory_read(
     return SendOKResponse();
   }
 
+  // Optional ";address_space:<hex>;" suffix (see the "address-spaces" 
feature).
+  lldb::addr_space_t address_space = 0;
+  if (m_address_space_suffix_supported && packet.GetBytesLeft() > 0 &&
+      packet.GetChar() == ';') {
+    llvm::StringRef name, value;
+    while (packet.GetNameColonValue(name, value)) {
+      if (name == "address_space" && value.getAsInteger(16, address_space))
+        return SendIllFormedResponse(packet, "invalid address_space suffix");
+    }
+  }
+
   // Allocate the response buffer.
   std::string buf(byte_count, '\0');
   if (buf.empty())
@@ -2679,11 +2693,12 @@ GDBRemoteCommunicationServerLLGS::Handle_memory_read(
   // Retrieve the process memory.
   size_t bytes_read = 0;
   Status error = m_current_process->ReadMemoryWithoutTrap(
-      read_addr, &buf[0], byte_count, bytes_read);
-  LLDB_LOG(
-      log,
-      "ReadMemoryWithoutTrap({0}) read {1} of {2} requested bytes (error: 
{3})",
-      read_addr, byte_count, bytes_read, error);
+      ProcessAddress(read_addr, address_space), &buf[0], byte_count,
+      bytes_read);
+  LLDB_LOG(log,
+           "read {1} of {2} requested bytes at {0:x} in address_space {4} "
+           "(error: {3})",
+           read_addr, byte_count, bytes_read, error, address_space);
   if (bytes_read == 0)
     return SendErrorResponse(0x08);
 
@@ -3915,6 +3930,28 @@ GDBRemoteCommunicationServerLLGS::Handle_jThreadsInfo(
   return SendPacketNoLock(escaped_response.GetString());
 }
 
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jAddressSpacesInfo(
+    StringExtractorGDBRemote &packet) {
+  Log *log = GetLog(LLDBLog::Process);
+
+  // Ensure we have a process.
+  if (!m_current_process ||
+      (m_current_process->GetID() == LLDB_INVALID_PROCESS_ID)) {
+    LLDB_LOG(log, "failed, no process available");
+    return SendErrorResponse(Status::FromErrorString("invalid process"));
+  }
+
+  std::vector<AddressSpaceInfo> address_spaces =
+      m_current_process->GetAddressSpaces();
+  if (address_spaces.empty())
+    return SendUnimplementedResponse(packet.GetStringRef().data());
+
+  StreamGDBRemote response;
+  response.PutAsJSONArray(address_spaces, /*hex_ascii=*/false);
+  return SendPacketNoLock(response.GetString());
+}
+
 GDBRemoteCommunication::PacketResult
 GDBRemoteCommunicationServerLLGS::Handle_qWatchpointSupportInfo(
     StringExtractorGDBRemote &packet) {
@@ -4502,6 +4539,10 @@ std::vector<std::string> 
GDBRemoteCommunicationServerLLGS::HandleFeatures(
     ret.push_back("memory-tagging+");
   if (bool(plugin_features & Extension::savecore))
     ret.push_back("qSaveCore+");
+  if (bool(plugin_features & Extension::address_spaces)) {
+    ret.push_back("address-spaces+");
+    m_address_space_suffix_supported = true;
+  }
   if (!m_accelerator_plugins.empty())
     ret.push_back("accelerator-plugins+");
 
diff --git 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index e5b4c9ec0bed0..d4d48d41bb5a8 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -137,6 +137,7 @@ class GDBRemoteCommunicationServerLLGS
   std::unordered_map<uint32_t, lldb::DataBufferSP> m_saved_registers_map;
   uint32_t m_next_saved_registers_id = 1;
   bool m_thread_suffix_supported = false;
+  bool m_address_space_suffix_supported = false;
   bool m_list_threads_in_stop_reply = false;
   bool m_non_stop = false;
   bool m_disabling_non_stop = false;
@@ -268,6 +269,8 @@ class GDBRemoteCommunicationServerLLGS
 
   PacketResult Handle_jThreadsInfo(StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_jAddressSpacesInfo(StringExtractorGDBRemote &packet);
+
   PacketResult Handle_qWatchpointSupportInfo(StringExtractorGDBRemote &packet);
 
   PacketResult Handle_qFileLoadAddress(StringExtractorGDBRemote &packet);
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index d8005ebfcea72..c1b0e206a4010 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -1219,6 +1219,10 @@ void ProcessGDBRemote::LoadStubBinaries() {
   }
 }
 
+void ProcessGDBRemote::DoResolveAddressSpaces() {
+  m_address_spaces = m_gdb_comm.GetAddressSpaces();
+}
+
 void ProcessGDBRemote::MaybeLoadExecutableModule() {
   ModuleSP module_sp = GetTarget().GetExecutableModule();
   if (!module_sp)
@@ -2910,6 +2914,13 @@ size_t ProcessGDBRemote::DoReadMemory(const 
ProcessAddress &process_addr,
   using xPacketState = GDBRemoteCommunicationClient::xPacketState;
 
   lldb::addr_t addr = process_addr.GetValue();
+  lldb::addr_space_t addr_space = process_addr.GetAddressSpace();
+  if (addr_space != LLDB_DEFAULT_ADDRESS_SPACE_ID &&
+      !m_gdb_comm.GetAddressSpacesSupported()) {
+    error = Status::FromErrorString("address spaces are not supported");
+    return 0;
+  }
+
   GetMaxMemorySize();
   xPacketState x_state = m_gdb_comm.GetxPacketState();
 
@@ -2924,11 +2935,19 @@ size_t ProcessGDBRemote::DoReadMemory(const 
ProcessAddress &process_addr,
     size = max_memory_size;
   }
 
-  char packet[64];
+  // A non-default address space rides on a ";address_space:<id>;" suffix.
+  char packet[128];
   int packet_len;
-  packet_len = ::snprintf(packet, sizeof(packet), "%c%" PRIx64 ",%" PRIx64,
-                          x_state != xPacketState::Unimplemented ? 'x' : 'm',
-                          (uint64_t)addr, (uint64_t)size);
+  if (addr_space != LLDB_DEFAULT_ADDRESS_SPACE_ID)
+    packet_len =
+        ::snprintf(packet, sizeof(packet),
+                   "%c%" PRIx64 ",%" PRIx64 ";address_space:%" PRIx64 ";",
+                   x_state != xPacketState::Unimplemented ? 'x' : 'm',
+                   (uint64_t)addr, (uint64_t)size, addr_space);
+  else
+    packet_len = ::snprintf(packet, sizeof(packet), "%c%" PRIx64 ",%" PRIx64,
+                            x_state != xPacketState::Unimplemented ? 'x' : 'm',
+                            (uint64_t)addr, (uint64_t)size);
   assert(packet_len + 1 < (int)sizeof(packet));
   UNUSED_IF_ASSERT_DISABLED(packet_len);
   StringExtractorGDBRemote response;
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
index f132858e14ca7..ebe342f684824 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -141,6 +141,8 @@ class ProcessGDBRemote : public Process,
   size_t DoReadMemory(const ProcessAddress &process_addr, void *buf,
                       size_t size, Status &error) override;
 
+  void DoResolveAddressSpaces() override;
+
   /// Override of DoReadMemoryRanges that uses MultiMemRead to perform this
   /// operation in a single packet.
   llvm::SmallVector<llvm::MutableArrayRef<uint8_t>>
diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp
index 1aeb3f0591f53..edbc4cbeef586 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -2037,11 +2037,23 @@ Status 
Process::DisableSoftwareBreakpoint(BreakpointSite *bp_site) {
 
 size_t Process::ReadMemory(const ProcessAddress &process_addr, void *buf,
                            size_t size, Status &error) {
+  error.Clear();
+
+  // Non-default address spaces bypass the flat memory cache.
+  if (!process_addr.IsInDefaultAddressSpace()) {
+    llvm::Expected<AddressSpaceInfo> info =
+        GetAddressSpaceInfo(process_addr.GetAddressSpace());
+    if (!info) {
+      error = Status::FromError(info.takeError());
+      return 0;
+    }
+    return DoReadMemory(process_addr, buf, size, error);
+  }
+
   lldb::addr_t addr = process_addr.GetValue();
   if (ABISP abi_sp = GetABI())
     addr = abi_sp->FixAnyAddress(addr);
 
-  error.Clear();
   if (!GetDisableMemoryCache()) {
 #if defined(VERIFY_MEMORY_READS)
     // Memory caching is enabled, with debug verification
@@ -7105,3 +7117,55 @@ void Process::SetAddressableBitMasks(AddressableBits 
bit_masks) {
     SetHighmemDataAddressMask(high_addr_mask);
   }
 }
+
+void Process::ResolveAddressSpaces() {
+  llvm::call_once(m_address_spaces_resolved,
+                  [this] { DoResolveAddressSpaces(); });
+}
+
+llvm::Expected<AddressSpaceInfo>
+Process::GetAddressSpaceInfo(llvm::StringRef address_space_name) {
+  ResolveAddressSpaces();
+  if (m_address_spaces.empty())
+    return llvm::createStringError("process doesn't support address spaces");
+
+  for (const auto &address_space_info : m_address_spaces) {
+    if (address_space_info.name == address_space_name.str())
+      return address_space_info;
+  }
+
+  std::string error_str("invalid address space \"");
+  error_str.append(address_space_name.str());
+  error_str.append("\", address space must be one of:");
+  bool first = true;
+  for (const auto &addr_space_info : m_address_spaces) {
+    if (!first)
+      error_str.append(",");
+    error_str.append(" \"");
+    error_str.append(addr_space_info.name);
+    error_str.append("\"");
+    first = false;
+  }
+  return llvm::createStringError(error_str.c_str());
+}
+
+llvm::Expected<AddressSpaceInfo>
+Process::GetAddressSpaceInfo(lldb::addr_space_t address_space_id) {
+  ResolveAddressSpaces();
+  if (m_address_spaces.empty())
+    return llvm::createStringError("process doesn't support address spaces");
+
+  for (const auto &address_space_info : m_address_spaces) {
+    if (address_space_info.space_id == address_space_id)
+      return address_space_info;
+  }
+
+  std::string error_str("invalid address space id, valid ids are:");
+  bool first = true;
+  for (const auto &addr_space_info : m_address_spaces) {
+    error_str.append(first ? " " : ", ");
+    error_str.append(std::to_string(addr_space_info.space_id));
+    first = false;
+  }
+  return llvm::createStringError(error_str.c_str());
+}
diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp 
b/lldb/source/Utility/StringExtractorGDBRemote.cpp
index 6fc3b63e02dd1..20237aec70c50 100644
--- a/lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -321,6 +321,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
       return eServerPacketType_jSignalsInfo;
     if (PACKET_MATCHES("jThreadsInfo"))
       return eServerPacketType_jThreadsInfo;
+    if (PACKET_MATCHES("jAddressSpacesInfo"))
+      return eServerPacketType_jAddressSpacesInfo;
 
     if (PACKET_MATCHES("jLLDBTraceSupported"))
       return eServerPacketType_jLLDBTraceSupported;
diff --git 
a/lldb/test/API/functionalities/gdb_remote_client/TestAddressSpaceMemoryRead.py 
b/lldb/test/API/functionalities/gdb_remote_client/TestAddressSpaceMemoryRead.py
new file mode 100644
index 0000000000000..b5e302a86ecb4
--- /dev/null
+++ 
b/lldb/test/API/functionalities/gdb_remote_client/TestAddressSpaceMemoryRead.py
@@ -0,0 +1,75 @@
+import lldb
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test.decorators import *
+from lldbsuite.test.gdbclientutils import *
+from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
+
+
+class TestAddressSpaceMemoryRead(GDBRemoteTestBase):
+    """
+    End-to-end test that the same numeric address read from two different
+    address spaces returns different bytes. The server advertises
+    "address-spaces+" in qSupported, reports the spaces via 
"jAddressSpacesInfo",
+    and reads memory with an optional ";address_space:<id>;" suffix on the
+    standard memory packet.
+    """
+
+    def test(self):
+        address_spaces_json = (
+            '[{"name":"global","space_id":1,"is_thread_specific":false},'
+            '{"name":"local","space_id":16,"is_thread_specific":true}]'
+        )
+
+        class MyResponder(MockGDBServerResponder):
+            def qSupported(self, client_supported):
+                return "PacketSize=3fff;QStartNoAckMode+;address-spaces+"
+
+            def qHostInfo(self):
+                return "ptrsize:8;endian:little;"
+
+            def _bytes_for_space(self, space):
+                if space == 1:
+                    return "aabbccdd"
+                if space == 16:
+                    return "11223344"
+                return "E01"
+
+            def _respond_impl(self, packet):
+                # The base dispatcher can't parse the ";address_space:<id>;"
+                # suffix, so handle suffixed reads here.
+                if packet and packet[0] in ("m", "x") and "address_space:" in 
packet:
+                    space = 0
+                    for field in packet[1:].split(";"):
+                        key, _, value = field.partition(":")
+                        if key == "address_space":
+                            space = int(value, 16)
+                    return self._bytes_for_space(space)
+                return super()._respond_impl(packet)
+
+            def x(self, addr, length):
+                # Force the client onto the hex "m" read path.
+                return ""
+
+            def other(self, packet):
+                if packet == "jAddressSpacesInfo":
+                    return escape_binary(address_spaces_json)
+                return ""
+
+        self.server.responder = MyResponder()
+        target = self.dbg.CreateTarget("")
+        process = self.connect(target)
+
+        error = lldb.SBError()
+
+        # Same numeric address, two spaces (global == id 1, local == id 16).
+        # Id 16 also pins the suffix encoding: it is sent as hex "10".
+        global_bytes = process.ReadMemory(lldb.SBProcessAddress(0x1000, 1), 4, 
error)
+        self.assertSuccess(error)
+        self.assertEqual(global_bytes, b"\xaa\xbb\xcc\xdd")
+
+        local_bytes = process.ReadMemory(lldb.SBProcessAddress(0x1000, 16), 4, 
error)
+        self.assertSuccess(error)
+        self.assertEqual(local_bytes, b"\x11\x22\x33\x44")
+
+        # Same address, different address space, different bytes.
+        self.assertNotEqual(global_bytes, local_bytes)
diff --git a/lldb/test/API/tools/lldb-server/TestGdbRemoteAddressSpaces.py 
b/lldb/test/API/tools/lldb-server/TestGdbRemoteAddressSpaces.py
new file mode 100644
index 0000000000000..219ae097914b5
--- /dev/null
+++ b/lldb/test/API/tools/lldb-server/TestGdbRemoteAddressSpaces.py
@@ -0,0 +1,28 @@
+import gdbremote_testcase
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+
+
+class TestGdbRemoteAddressSpaces(gdbremote_testcase.GdbRemoteTestCaseBase):
+    def test_qSupported_no_address_spaces_by_default(self):
+        self.build()
+        self.set_inferior_startup_launch()
+        self.prep_debug_monitor_and_inferior()
+        self.add_qSupported_packets()
+        features = 
self.parse_qSupported_response(self.expect_gdbremote_sequence())
+        # A process with no address spaces does not advertise 
"address-spaces+".
+        self.assertNotIn("address-spaces", features)
+
+    def test_jAddressSpacesInfo_empty_by_default(self):
+        self.build()
+        self.set_inferior_startup_launch()
+        self.prep_debug_monitor_and_inferior()
+
+        self.test_sequence.add_log_lines(
+            [
+                "read packet: $jAddressSpacesInfo#00",
+                "send packet: $#00",
+            ],
+            True,
+        )
+        self.expect_gdbremote_sequence()
diff --git 
a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp 
b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
index 3ec212b1c205d..bcb14798884ab 100644
--- a/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
+++ b/lldb/unittests/Process/gdb-remote/GDBRemoteCommunicationClientTest.cpp
@@ -11,7 +11,9 @@
 #include "lldb/Host/ConnectionFileDescriptor.h"
 #include "lldb/Host/XML.h"
 #include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Utility/AddressSpace.h"
 #include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/GDBRemote.h"
 #include "lldb/Utility/StructuredData.h"
 #include "lldb/lldb-enumerations.h"
 #include "llvm/ADT/ArrayRef.h"
@@ -191,6 +193,54 @@ TEST_F(GDBRemoteCommunicationClientTest, ReadRegister) {
             memcmp(buffer_sp->GetBytes(), all_registers, sizeof 
all_registers));
 }
 
+// Advertise "address-spaces+" so the client enables address-space packets.
+static void EnableAddressSpaces(GDBRemoteCommunicationClient &client,
+                                MockServer &server) {
+  std::future<void> result =
+      std::async(std::launch::async, [&] { client.GetRemoteQSupported(); });
+  HandlePacket(server, testing::StartsWith("qSupported:"),
+               "PacketSize=3fff;address-spaces+");
+  result.get();
+}
+
+TEST_F(GDBRemoteCommunicationClientTest, GetAddressSpaces) {
+  EnableAddressSpaces(client, server);
+  std::future<std::vector<AddressSpaceInfo>> result =
+      std::async(std::launch::async, [&] { return client.GetAddressSpaces(); 
});
+  StreamGDBRemote escaped;
+  llvm::StringRef json =
+      R"([{"name":"global","space_id":1,"is_thread_specific":false},)"
+      R"({"name":"local","space_id":2,"is_thread_specific":true}])";
+  escaped.PutEscapedBytes(json.data(), json.size());
+  HandlePacket(server, "jAddressSpacesInfo", escaped.GetString());
+
+  std::vector<AddressSpaceInfo> spaces = result.get();
+  ASSERT_EQ(spaces.size(), 2u);
+  EXPECT_EQ(spaces[0].name, "global");
+  EXPECT_EQ(spaces[0].space_id, 1u);
+  EXPECT_FALSE(spaces[0].is_thread_specific);
+  EXPECT_EQ(spaces[1].name, "local");
+  EXPECT_EQ(spaces[1].space_id, 2u);
+  EXPECT_TRUE(spaces[1].is_thread_specific);
+}
+
+TEST_F(GDBRemoteCommunicationClientTest, GetAddressSpacesNotSupported) {
+  // Without "address-spaces+" in qSupported the client never sends the packet.
+  EXPECT_TRUE(client.GetAddressSpaces().empty());
+}
+
+TEST_F(GDBRemoteCommunicationClientTest, GetAddressSpacesMalformed) {
+  EnableAddressSpaces(client, server);
+  std::future<std::vector<AddressSpaceInfo>> result =
+      std::async(std::launch::async, [&] { return client.GetAddressSpaces(); 
});
+  // Missing required fields, so parsing fails and the client returns empty.
+  StreamGDBRemote escaped;
+  llvm::StringRef malformed = R"([{"name":"global"}])";
+  escaped.PutEscapedBytes(malformed.data(), malformed.size());
+  HandlePacket(server, "jAddressSpacesInfo", escaped.GetString());
+  EXPECT_TRUE(result.get().empty());
+}
+
 TEST_F(GDBRemoteCommunicationClientTest, SaveRestoreRegistersNoSuffix) {
   const lldb::tid_t tid = 0x47;
   uint32_t save_id;

_______________________________________________
lldb-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits

Reply via email to