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

>From 288abf7100df5fcb789b200cc61dfff0ad5499c7 Mon Sep 17 00:00:00 2001
From: satya janga <[email protected]>
Date: Tue, 4 Aug 2026 12:15:29 -0700
Subject: [PATCH 1/3] [lldb] Add AddressSpaceInfo and ProcessAddress utility
 classes

Add the two value types used by the upcoming generic address space
support. Nothing uses them yet; adopting them is done separately.

AddressSpaceInfo describes a single address space exposed by a process:
a name, a numeric id, and whether it is thread specific. It has JSON
serialization so it can be carried over the gdb-remote protocol.

ProcessAddress pairs an address with an address space id.
LLDB_DEFAULT_ADDRESS_SPACE_ID (0) is the default (flat) address space,
so a ProcessAddress with no explicit space behaves like a plain
lldb::addr_t. The constructor from lldb::addr_t is implicit so that
existing call sites keep working when the read memory APIs adopt it.
---
 lldb/include/lldb/Utility/AddressSpace.h    | 34 +++++++++++++
 lldb/include/lldb/Utility/ProcessAddress.h  | 53 +++++++++++++++++++
 lldb/include/lldb/lldb-defines.h            |  2 +
 lldb/include/lldb/lldb-forward.h            |  1 +
 lldb/source/Utility/AddressSpace.cpp        | 28 +++++++++++
 lldb/source/Utility/CMakeLists.txt          |  1 +
 lldb/unittests/Utility/AddressSpaceTest.cpp | 56 +++++++++++++++++++++
 lldb/unittests/Utility/CMakeLists.txt       |  1 +
 8 files changed, 176 insertions(+)
 create mode 100644 lldb/include/lldb/Utility/AddressSpace.h
 create mode 100644 lldb/include/lldb/Utility/ProcessAddress.h
 create mode 100644 lldb/source/Utility/AddressSpace.cpp
 create mode 100644 lldb/unittests/Utility/AddressSpaceTest.cpp

diff --git a/lldb/include/lldb/Utility/AddressSpace.h 
b/lldb/include/lldb/Utility/AddressSpace.h
new file mode 100644
index 0000000000000..ce5119f573d8d
--- /dev/null
+++ b/lldb/include/lldb/Utility/AddressSpace.h
@@ -0,0 +1,34 @@
+//===-- AddressSpace.h 
----------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_UTILITY_ADDRESSSPACE_H
+#define LLDB_UTILITY_ADDRESSSPACE_H
+
+#include "lldb/lldb-types.h"
+#include "llvm/Support/JSON.h"
+#include <string>
+#include <vector>
+
+namespace lldb_private {
+
+/// A single address space reported by a process (see the "jAddressSpacesInfo"
+/// packet in docs/resources/lldbgdbremote.md).
+struct AddressSpaceInfo {
+  std::string name;
+  uint64_t space_id = 0;
+  bool is_thread_specific = false;
+};
+
+bool fromJSON(const llvm::json::Value &value, AddressSpaceInfo &data,
+              llvm::json::Path path);
+
+llvm::json::Value toJSON(const AddressSpaceInfo &data);
+
+} // namespace lldb_private
+
+#endif // LLDB_UTILITY_ADDRESSSPACE_H
diff --git a/lldb/include/lldb/Utility/ProcessAddress.h 
b/lldb/include/lldb/Utility/ProcessAddress.h
new file mode 100644
index 0000000000000..c7c8a062760b9
--- /dev/null
+++ b/lldb/include/lldb/Utility/ProcessAddress.h
@@ -0,0 +1,53 @@
+//===-- ProcessAddress.h 
--------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_UTILITY_PROCESSADDRESS_H
+#define LLDB_UTILITY_PROCESSADDRESS_H
+
+#include "lldb/lldb-defines.h"
+#include "lldb/lldb-types.h"
+#include <optional>
+
+namespace lldb_private {
+
+/// An address in a process, qualified by an address space.
+///
+/// The address space is a numeric id reported by the process (see
+/// Process::GetAddressSpaces). LLDB_DEFAULT_ADDRESS_SPACE_ID is the default
+/// (flat) address space, so a ProcessAddress with no space behaves like a 
plain
+/// lldb::addr_t.
+class ProcessAddress {
+  lldb::addr_t m_value;
+  uint64_t m_addr_space = LLDB_DEFAULT_ADDRESS_SPACE_ID;
+  /// If this has a value, then this is a thread specific address. Addresses in
+  /// a thread specific address space (see AddressSpaceInfo) are only 
meaningful
+  /// together with the thread they belong to.
+  std::optional<lldb::tid_t> m_tid;
+
+public:
+  /// Implicit so existing lldb::addr_t call sites keep working.
+  ProcessAddress(lldb::addr_t load_addr) : m_value(load_addr) {}
+
+  ProcessAddress(lldb::addr_t addr, uint64_t addr_space,
+                 std::optional<lldb::tid_t> tid = std::nullopt)
+      : m_value(addr), m_addr_space(addr_space), m_tid(tid) {}
+
+  bool IsInDefaultAddressSpace() const {
+    return m_addr_space == LLDB_DEFAULT_ADDRESS_SPACE_ID;
+  }
+
+  lldb::addr_t GetValue() const { return m_value; }
+
+  uint64_t GetAddressSpace() const { return m_addr_space; }
+
+  std::optional<lldb::tid_t> GetThreadID() const { return m_tid; }
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_UTILITY_PROCESSADDRESS_H
diff --git a/lldb/include/lldb/lldb-defines.h b/lldb/include/lldb/lldb-defines.h
index e3f88c4681a53..347b5dfb3434d 100644
--- a/lldb/include/lldb/lldb-defines.h
+++ b/lldb/include/lldb/lldb-defines.h
@@ -80,6 +80,8 @@
 /// Invalid value definitions
 #define LLDB_INVALID_STOP_ID 0
 #define LLDB_INVALID_ADDRESS UINT64_MAX
+#define LLDB_DEFAULT_ADDRESS_SPACE_ID 0
+#define LLDB_INVALID_ADDRESS_SPACE_ID UINT64_MAX
 #define LLDB_INVALID_INDEX32 UINT32_MAX
 #define LLDB_INVALID_INDEX64 UINT64_MAX
 #define LLDB_INVALID_IVAR_OFFSET UINT32_MAX
diff --git a/lldb/include/lldb/lldb-forward.h b/lldb/include/lldb/lldb-forward.h
index 47362915d6a56..f691b5a3a76e9 100644
--- a/lldb/include/lldb/lldb-forward.h
+++ b/lldb/include/lldb/lldb-forward.h
@@ -22,6 +22,7 @@ class AddressRange;
 class AddressRanges;
 class AddressRangeList;
 class AddressResolver;
+class ProcessAddress;
 class ArchSpec;
 class Architecture;
 class Args;
diff --git a/lldb/source/Utility/AddressSpace.cpp 
b/lldb/source/Utility/AddressSpace.cpp
new file mode 100644
index 0000000000000..b6fc9335e8113
--- /dev/null
+++ b/lldb/source/Utility/AddressSpace.cpp
@@ -0,0 +1,28 @@
+//===-- AddressSpace.cpp 
--------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/AddressSpace.h"
+
+using namespace llvm;
+using namespace llvm::json;
+
+namespace lldb_private {
+
+bool fromJSON(const json::Value &value, AddressSpaceInfo &data, Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("name", data.name) && o.map("space_id", data.space_id) &&
+         o.map("is_thread_specific", data.is_thread_specific);
+}
+
+json::Value toJSON(const AddressSpaceInfo &data) {
+  return json::Value(Object{{"name", data.name},
+                            {"space_id", data.space_id},
+                            {"is_thread_specific", data.is_thread_specific}});
+}
+
+} // namespace lldb_private
diff --git a/lldb/source/Utility/CMakeLists.txt 
b/lldb/source/Utility/CMakeLists.txt
index 8efcbe47dd19b..75749d60c4d0f 100644
--- a/lldb/source/Utility/CMakeLists.txt
+++ b/lldb/source/Utility/CMakeLists.txt
@@ -25,6 +25,7 @@ endif()
 
 add_lldb_library(lldbUtility NO_INTERNAL_DEPENDENCIES
   AddressableBits.cpp
+  AddressSpace.cpp
   ArchSpec.cpp
   Args.cpp
   Baton.cpp
diff --git a/lldb/unittests/Utility/AddressSpaceTest.cpp 
b/lldb/unittests/Utility/AddressSpaceTest.cpp
new file mode 100644
index 0000000000000..5a5ccfd1b5b81
--- /dev/null
+++ b/lldb/unittests/Utility/AddressSpaceTest.cpp
@@ -0,0 +1,56 @@
+//===-- AddressSpaceTest.cpp 
----------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "lldb/Utility/AddressSpace.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+using namespace lldb_private;
+
+static std::string ToString(const llvm::json::Value &value) {
+  return llvm::formatv("{0}", value).str();
+}
+
+TEST(AddressSpaceTest, RoundTrip) {
+  AddressSpaceInfo info{"local", 2, /*is_thread_specific=*/true};
+  llvm::Expected<AddressSpaceInfo> parsed = 
llvm::json::parse<AddressSpaceInfo>(
+      ToString(toJSON(info)), "AddressSpaceInfo");
+  ASSERT_THAT_EXPECTED(parsed, llvm::Succeeded());
+  EXPECT_EQ(parsed->name, "local");
+  EXPECT_EQ(parsed->space_id, 2u);
+  EXPECT_TRUE(parsed->is_thread_specific);
+}
+
+TEST(AddressSpaceTest, ArrayRoundTrip) {
+  std::vector<AddressSpaceInfo> spaces = {
+      {"global", 1, false},
+      {"local", 2, true},
+      {"private", 3, false},
+  };
+  llvm::json::Array array;
+  for (const AddressSpaceInfo &space : spaces)
+    array.push_back(toJSON(space));
+
+  llvm::Expected<std::vector<AddressSpaceInfo>> parsed =
+      llvm::json::parse<std::vector<AddressSpaceInfo>>(
+          ToString(llvm::json::Value(std::move(array))), "AddressSpaceInfo");
+  ASSERT_THAT_EXPECTED(parsed, llvm::Succeeded());
+  ASSERT_EQ(parsed->size(), 3u);
+  EXPECT_EQ((*parsed)[1].name, "local");
+  EXPECT_EQ((*parsed)[1].space_id, 2u);
+  EXPECT_TRUE((*parsed)[1].is_thread_specific);
+  EXPECT_FALSE((*parsed)[0].is_thread_specific);
+}
+
+TEST(AddressSpaceTest, MissingFieldFails) {
+  // "space_id" is required.
+  llvm::Expected<AddressSpaceInfo> parsed = 
llvm::json::parse<AddressSpaceInfo>(
+      R"({"name":"global"})", "AddressSpaceInfo");
+  EXPECT_THAT_EXPECTED(parsed, llvm::Failed());
+}
diff --git a/lldb/unittests/Utility/CMakeLists.txt 
b/lldb/unittests/Utility/CMakeLists.txt
index ed159748838b5..e46a1774f020d 100644
--- a/lldb/unittests/Utility/CMakeLists.txt
+++ b/lldb/unittests/Utility/CMakeLists.txt
@@ -1,5 +1,6 @@
 add_lldb_unittest(UtilityTests
   AcceleratorGDBRemotePacketsTest.cpp
+  AddressSpaceTest.cpp
   AnsiTerminalTest.cpp
   ArgsTest.cpp
   OptionsWithRawTest.cpp

>From 59e4ee4bee37b347b72f3f9639f3752ea155bfb7 Mon Sep 17 00:00:00 2001
From: satya janga <[email protected]>
Date: Tue, 4 Aug 2026 12:21:36 -0700
Subject: [PATCH 2/3] [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    |  5 ++++-
 .../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 |  7 +++++--
 .../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, 147 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 9c7e66cb79028..21c5920ecee28 100644
--- a/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp
+++ b/lldb/source/Plugins/Process/AIX/NativeProcessAIX.cpp
@@ -238,8 +238,11 @@ 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) {
+  lldb::addr_t addr = process_addr.GetValue();
+  (void)addr;
   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 4853ab2827d9e..39ce514ac3c26 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 80f1b5662ba61..0f793d1c3fa5c 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 3fd14c4c43071..8224345b67394 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 4cc760de54a5c..a36461bad1b1f 100644
--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
+++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.cpp
@@ -453,8 +453,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);
 
@@ -514,8 +515,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 846d8cb91cadf..cfb0a772bfed7 100644
--- a/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
+++ b/lldb/source/Plugins/Process/elf-core/ProcessElfCore.h
@@ -79,11 +79,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 724e7f2e71bd8..6700d85d6f5c6 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -2902,10 +2902,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 ca75899bc5cbf..85db0fc051979 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -137,8 +137,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 d0b9de0091511..8acdf75028292 100644
--- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
+++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp
@@ -712,15 +712,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 e119b3e3ecf6d..10d7d8ca587d8 100644
--- a/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
+++ b/lldb/source/Plugins/Process/wasm/ProcessWasm.cpp
@@ -125,8 +125,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..9659db1876ad3 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
@@ -59,8 +59,11 @@ 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) {
+  lldb::addr_t addr = process_addr.GetValue();
+  (void)addr;
   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 f89e9215de713..0ab48a9a8acb7 100644
--- a/lldb/unittests/Target/MemoryTest.cpp
+++ b/lldb/unittests/Target/MemoryTest.cpp
@@ -91,8 +91,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;
 
@@ -463,8 +463,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)
@@ -629,8 +630,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;
@@ -794,7 +796,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 6a930b2603d6b92bfc046f7ffb8e09c9ac753ca3 Mon Sep 17 00:00:00 2001
From: satya janga <[email protected]>
Date: Tue, 4 Aug 2026 12:28:23 -0700
Subject: [PATCH 3/3] [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          | 71 ++++++++++++++++++
 lldb/include/lldb/API/SBAddress.h             | 27 +++++++
 lldb/include/lldb/API/SBDefines.h             |  1 +
 lldb/include/lldb/API/SBProcess.h             |  9 +++
 .../lldb/Host/common/NativeProcessProtocol.h  |  9 ++-
 lldb/include/lldb/Target/Process.h            | 22 ++++++
 .../lldb/Utility/StringExtractorGDBRemote.h   |  1 +
 .../tools/lldb-server/gdbremote_testcase.py   |  1 +
 lldb/source/API/SBAddress.cpp                 | 31 ++++++++
 lldb/source/API/SBProcess.cpp                 | 50 +++++++++++++
 .../GDBRemoteCommunicationClient.cpp          | 32 ++++++++
 .../gdb-remote/GDBRemoteCommunicationClient.h | 10 +++
 .../GDBRemoteCommunicationServerLLGS.cpp      | 51 +++++++++++--
 .../GDBRemoteCommunicationServerLLGS.h        |  3 +
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 28 ++++++-
 .../Process/gdb-remote/ProcessGDBRemote.h     |  2 +
 lldb/source/Target/Process.cpp                | 67 ++++++++++++++++-
 .../Utility/StringExtractorGDBRemote.cpp      |  2 +
 .../TestAddressSpaceMemoryRead.py             | 74 +++++++++++++++++++
 .../lldb-server/TestGdbRemoteAddressSpaces.py | 28 +++++++
 .../GDBRemoteCommunicationClientTest.cpp      | 51 +++++++++++++
 22 files changed, 565 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 072e688c4bde1..ae0afa58995b4 100644
--- a/lldb/bindings/python/python-typemaps.swig
+++ b/lldb/bindings/python/python-typemaps.swig
@@ -310,6 +310,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..24955c709e867 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -822,6 +822,73 @@ 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`. 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:2;
+read packet: $<binary encoding of the 4 bytes at 0x1000 in address space 2>
+```
+
+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 +2784,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..c474d7e8b6fdb 100644
--- a/lldb/include/lldb/API/SBAddress.h
+++ b/lldb/include/lldb/API/SBAddress.h
@@ -130,6 +130,33 @@ class LLDB_API SBAddress {
 bool LLDB_API operator==(const SBAddress &lhs, const SBAddress &rhs);
 #endif
 
+/// A memory address that can name a non-default address space (see
+/// lldb_private::ProcessAddress).
+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, uint64_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..e8fda28423628 100644
--- a/lldb/include/lldb/API/SBProcess.h
+++ b/lldb/include/lldb/API/SBProcess.h
@@ -199,6 +199,15 @@ class LLDB_API SBProcess {
 
   size_t ReadMemory(addr_t addr, void *buf, size_t size, lldb::SBError &error);
 
+  /// Read memory described by an SBProcessAddress (which may name a 
non-default
+  /// address space). Returns the number of bytes read into \a buf.
+  size_t ReadMemory(SBProcessAddress process_addr, void *buf, size_t size,
+                    lldb::SBError &error);
+
+  /// Resolve an address space name to its numeric id for this process, or
+  /// return LLDB_INVALID_ADDRESS_SPACE_ID and set \a error if it is not valid.
+  uint64_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..687f7e68f4f97 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,11 @@ 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 that expose address spaces handle a non-default address space;
+  /// others should error on one.
   virtual Status ReadMemory(const ProcessAddress &addr, void *buf, size_t size,
                             size_t &bytes_read) = 0;
 
@@ -298,8 +304,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..49b36c1446e70 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);
 
+  /// The address spaces this process exposes; empty for single-space 
processes.
+  llvm::ArrayRef<AddressSpaceInfo> GetAddressSpaces() const {
+    return m_address_spaces;
+  }
+
+  llvm::Expected<AddressSpaceInfo>
+  GetAddressSpaceInfo(llvm::StringRef address_space_name);
+
+  llvm::Expected<AddressSpaceInfo>
+  GetAddressSpaceInfo(uint64_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,10 @@ 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; ///< Address spaces reported by the process plugin,
+                        /// 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..484165a66eefc 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,32 @@ 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, uint64_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..bda7ce899e18b 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,54 @@ 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());
+}
+
+uint64_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..4b3210ad95e48 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,13 @@ class GDBRemoteCommunicationClient : public 
GDBRemoteClientBase {
 
   std::vector<lldb::addr_t> GetProcessStandaloneBinaries();
 
+  /// Query the process for its address spaces via "jAddressSpacesInfo"; empty
+  /// if unsupported.
+  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 +615,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..5346f78f23ebd 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).
+  uint64_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(0, 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 6700d85d6f5c6..6d25f806cb585 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -1216,6 +1216,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)
@@ -2907,6 +2911,13 @@ size_t ProcessGDBRemote::DoReadMemory(const 
ProcessAddress &process_addr,
   using xPacketState = GDBRemoteCommunicationClient::xPacketState;
 
   lldb::addr_t addr = process_addr.GetValue();
+  uint64_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();
 
@@ -2921,11 +2932,20 @@ size_t ProcessGDBRemote::DoReadMemory(const 
ProcessAddress &process_addr,
     size = max_memory_size;
   }
 
-  char packet[64];
+  // A non-default address space rides on an optional ";address_space:<id>;"
+  // suffix on the standard m/x packet (see the "address-spaces" feature).
+  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:%" PRIu64 ";",
+                   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 85db0fc051979..88930b9925d69 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h
@@ -140,6 +140,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..eec6199904700 100644
--- a/lldb/source/Target/Process.cpp
+++ b/lldb/source/Target/Process.cpp
@@ -2037,11 +2037,24 @@ 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 and go straight
+  // to the process plugin.
+  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 +7118,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(uint64_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..c4af59c33df78
--- /dev/null
+++ 
b/lldb/test/API/functionalities/gdb_remote_client/TestAddressSpaceMemoryRead.py
@@ -0,0 +1,74 @@
+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":2,"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 == 2:
+                    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 2).
+        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, 2), 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..9fbc9dc716103 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,55 @@ TEST_F(GDBRemoteCommunicationClientTest, ReadRegister) {
             memcmp(buffer_sp->GetBytes(), all_registers, sizeof 
all_registers));
 }
 
+// Run the qSupported handshake with the server advertising "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