https://github.com/satyajanga created 
https://github.com/llvm/llvm-project/pull/217393

None

>From 92201e957f5af130f46ca1b0f5787727f688ce74 Mon Sep 17 00:00:00 2001
From: satya janga <[email protected]>
Date: Wed, 3 Jun 2026 13:20:04 -0700
Subject: [PATCH 1/2] [lldb-server] Add dynamic loader support to accelerator
 plugin protocol

Add jAcceleratorPluginGetDynamicLoaderLibraryInfo packet for querying
shared library information from accelerator plugins. This includes:

- AcceleratorSectionInfo for per-section load addresses
- AcceleratorDynamicLoaderLibraryInfo with pathname, UUID, load address,
  native memory location, file offset/size, and loaded sections
- AcceleratorDynamicLoaderArgs and AcceleratorDynamicLoaderResponse
- GetDynamicLoaderLibraryInfos virtual method on the plugin interface
- LLGS packet handler with plugin name routing
- Mock plugin implementation returning a test library
- Unit tests for all new JSON serialization types
- API test verifying the packet round-trip
---
 lldb/docs/resources/lldbgdbremote.md          | 66 +++++++++++++++
 .../Utility/AcceleratorGDBRemotePackets.h     | 66 ++++++++++++++-
 .../lldb/Utility/StringExtractorGDBRemote.h   |  1 +
 .../GDBRemoteCommunicationServerLLGS.cpp      | 33 ++++++++
 .../GDBRemoteCommunicationServerLLGS.h        |  3 +
 .../gdb-remote/LLDBServerAcceleratorPlugin.h  |  5 ++
 .../Utility/AcceleratorGDBRemotePackets.cpp   | 80 +++++++++++++++---
 .../Utility/StringExtractorGDBRemote.cpp      |  2 +
 .../mock/TestMockAcceleratorPackets.py        | 55 +++++++++++++
 .../Mock/LLDBServerMockAcceleratorPlugin.cpp  | 12 +++
 .../Mock/LLDBServerMockAcceleratorPlugin.h    |  3 +
 .../AcceleratorGDBRemotePacketsTest.cpp       | 81 +++++++++++++++++++
 12 files changed, 395 insertions(+), 12 deletions(-)

diff --git a/lldb/docs/resources/lldbgdbremote.md 
b/lldb/docs/resources/lldbgdbremote.md
index 93090c19c5ec0..2b64b663c9461 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -2862,3 +2862,69 @@ STUB REPLIES:  
{"disable_bp":true,"auto_resume_native":false,"actions":{"plugin_
 
 **Priority To Implement:** Required for hardware accelerator debugging
 support. Not needed for non-hardware-accelerator debugging.
+
+### jAcceleratorPluginGetDynamicLoaderLibraryInfo
+
+Requests shared library information from an accelerator plugin. The client
+sends this packet when it needs to load or update the accelerator's shared
+library list. This packet requires the `accelerator-plugins+` feature from
+`qSupported`.
+
+```
+LLDB SENDS:    jAcceleratorPluginGetDynamicLoaderLibraryInfo:<json>
+STUB REPLIES:  <json_response>
+```
+
+The request JSON has the following fields:
+
+| Key           | Type   | Description |
+|---------------|--------|-------------|
+| `plugin_name` | string | Name of the accelerator plugin to query. |
+| `full`        | bool   | If true, return every library the plugin knows 
about. If false, return only what changed since the last query. |
+
+A plugin may track thousands of code objects, so `full` lets a client that is
+already up to date ask only for the delta instead of re-receiving the whole
+list on every stop. It is the plugin that decides what "changed since the last
+query" means, and the plugin that keeps that state.
+
+Because that state lives in the plugin and is not per-client, a client cannot
+assume it starts from a known point: an earlier client may have consumed the
+pending changes with `full=false`. The first query of a session must therefore
+use `full=true`, and a client should also use it whenever it discards its own
+module list and needs to rebuild it.
+
+The response JSON is an object with a single `library_infos` key, holding an
+array of library info objects:
+
+| Key                     | Type   | Description |
+|-------------------------|--------|-------------|
+| `pathname`              | string | Path to the object file. |
+| `load`                  | bool   | True when the library is being loaded, 
false when it is being unloaded. |
+| `load_address`          | int    | (optional) Base address the whole object 
file is slid to. |
+| `loaded_sections`       | array  | (optional) Per-section load addresses, 
for object files whose sections load at independent addresses. Each entry has 
`names` (the section name, or a path of nested section names to descend) and 
`load_address`. |
+| `uuid`                  | string | (optional) UUID of the object file, if 
the plugin knows it. |
+| `native_memory_address` | int    | (optional) Address **in the native (host) 
process** where the object file image can be read, for a library that only 
exists in memory. |
+| `native_memory_size`    | int    | (optional) Size of that in-memory image. |
+| `file_offset`           | int    | (optional) Byte offset of the object file 
within `pathname`, for a library embedded in a containing file. |
+| `file_size`             | int    | (optional) Size of the object file within 
that containing file. |
+
+`load_address`, `loaded_sections` and neither-of-the-two are three different
+requests, and are resolved in that order:
+
+* `load_address` present: slide the whole object file to that address.
+* otherwise `loaded_sections` non-empty: load only the named sections, each at
+  its own address.
+* otherwise: load at the file addresses with no slide.
+
+An absent `loaded_sections` and an empty `loaded_sections` therefore mean the
+same thing here: no per-section addresses were supplied. A plugin that wants
+sections loaded must send a non-empty array.
+
+Example:
+```
+LLDB SENDS:    
jAcceleratorPluginGetDynamicLoaderLibraryInfo:{"plugin_name":"mock","full":true}
+STUB REPLIES:  
{"library_infos":[{"pathname":"/path/to/lib.so","load":true,"load_address":2130706432,"loaded_sections":[]}]}
+```
+
+**Priority To Implement:** Required for hardware accelerator debugging
+support. Not needed for non-hardware-accelerator debugging.
diff --git a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h 
b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
index faa7f5575f157..b804b216f51d0 100644
--- a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
@@ -9,6 +9,7 @@
 #ifndef LLDB_UTILITY_ACCELERATORGDBREMOTEPACKETS_H
 #define LLDB_UTILITY_ACCELERATORGDBREMOTEPACKETS_H
 
+#include "lldb/lldb-types.h"
 #include "llvm/Support/JSON.h"
 #include <cstdint>
 #include <optional>
@@ -21,7 +22,7 @@ struct SymbolValue {
   /// Symbol name as requested in AcceleratorBreakpointInfo::symbol_names.
   std::string name;
   /// Load address of the symbol in the native process, or nullopt if not 
found.
-  std::optional<uint64_t> value;
+  std::optional<lldb::addr_t> value;
 };
 
 bool fromJSON(const llvm::json::Value &value, SymbolValue &data,
@@ -41,7 +42,7 @@ llvm::json::Value toJSON(const AcceleratorBreakpointByName 
&data);
 
 struct AcceleratorBreakpointByAddress {
   /// Load address in the native debug target.
-  uint64_t load_address = 0;
+  lldb::addr_t load_address = 0;
 };
 
 bool fromJSON(const llvm::json::Value &value,
@@ -78,7 +79,7 @@ struct AcceleratorBreakpointHitArgs {
   AcceleratorBreakpointInfo breakpoint;
   std::vector<SymbolValue> symbol_values;
 
-  std::optional<uint64_t> GetSymbolValue(llvm::StringRef symbol_name) const;
+  std::optional<lldb::addr_t> GetSymbolValue(llvm::StringRef symbol_name) 
const;
 };
 
 bool fromJSON(const llvm::json::Value &value,
@@ -166,6 +167,65 @@ bool fromJSON(const llvm::json::Value &value,
               AcceleratorBreakpointHitResponse &data, llvm::json::Path path);
 llvm::json::Value toJSON(const AcceleratorBreakpointHitResponse &data);
 
+struct AcceleratorSectionInfo {
+  /// Each name is looked up as a child of the previous one, e.g.
+  /// ["PT_LOAD[0]", ".text"].
+  std::vector<std::string> names;
+  lldb::addr_t load_address = 0;
+};
+
+bool fromJSON(const llvm::json::Value &value, AcceleratorSectionInfo &data,
+              llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorSectionInfo &data);
+
+struct AcceleratorDynamicLoaderLibraryInfo {
+  std::string pathname;
+  std::optional<std::string> uuid_str;
+  /// False means unload.
+  bool load = true;
+  /// Slides the whole object file. If unset, use \a loaded_sections or the
+  /// file addresses.
+  std::optional<lldb::addr_t> load_address;
+  /// Used when sections load at independent addresses. Absent and empty mean
+  /// the same thing: no per-section addresses were supplied.
+  std::vector<AcceleratorSectionInfo> loaded_sections;
+  /// Where the image can be read in the native process, meaning the host
+  /// process driving the accelerator, not the accelerator itself. Set for a
+  /// library that only exists in memory.
+  std::optional<lldb::addr_t> native_memory_address;
+  std::optional<uint64_t> native_memory_size;
+  /// Slice of \a pathname holding the object file, when embedded in a
+  /// container.
+  std::optional<uint64_t> file_offset;
+  std::optional<uint64_t> file_size;
+};
+
+bool fromJSON(const llvm::json::Value &value,
+              AcceleratorDynamicLoaderLibraryInfo &data, llvm::json::Path 
path);
+llvm::json::Value toJSON(const AcceleratorDynamicLoaderLibraryInfo &data);
+
+/// Arguments for the jAcceleratorPluginGetDynamicLoaderLibraryInfo packet.
+struct AcceleratorDynamicLoaderArgs {
+  std::string plugin_name;
+  /// If false, return only what changed since the last query. That state lives
+  /// in the plugin rather than per-client, so the first query of a session
+  /// must use true.
+  bool full = true;
+};
+
+bool fromJSON(const llvm::json::Value &value,
+              AcceleratorDynamicLoaderArgs &data, llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorDynamicLoaderArgs &data);
+
+/// Response from the jAcceleratorPluginGetDynamicLoaderLibraryInfo packet.
+struct AcceleratorDynamicLoaderResponse {
+  std::vector<AcceleratorDynamicLoaderLibraryInfo> library_infos;
+};
+
+bool fromJSON(const llvm::json::Value &value,
+              AcceleratorDynamicLoaderResponse &data, llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorDynamicLoaderResponse &data);
+
 } // namespace lldb_private
 
 #endif // LLDB_UTILITY_ACCELERATORGDBREMOTEPACKETS_H
diff --git a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h 
b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
index 624a2febe857e..0236891dfcde1 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -176,6 +176,7 @@ class StringExtractorGDBRemote : public StringExtractor {
     eServerPacketType_jMultiBreakpoint,
     eServerPacketType_jAcceleratorPluginInitialize,
     eServerPacketType_jAcceleratorPluginBreakpointHit,
+    eServerPacketType_jAcceleratorPluginGetDynamicLoaderLibraryInfo,
 
     eServerPacketType_qMemTags, // read memory tags
     eServerPacketType_QMemTags, // write memory tags
diff --git 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 2e824282079b4..791c02fd96b20 100644
--- 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -233,6 +233,11 @@ void 
GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
           eServerPacketType_jAcceleratorPluginBreakpointHit,
       &GDBRemoteCommunicationServerLLGS::
           Handle_jAcceleratorPluginBreakpointHit);
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::
+          eServerPacketType_jAcceleratorPluginGetDynamicLoaderLibraryInfo,
+      &GDBRemoteCommunicationServerLLGS::
+          Handle_jAcceleratorPluginGetDynamicLoaderLibraryInfo);
 
   RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g,
                                 &GDBRemoteCommunicationServerLLGS::Handle_g);
@@ -4643,3 +4648,31 @@ 
GDBRemoteCommunicationServerLLGS::Handle_jAcceleratorPluginBreakpointHit(
   return SendErrorResponse(
       Status::FromErrorString("unknown accelerator plugin name"));
 }
+
+GDBRemoteCommunication::PacketResult GDBRemoteCommunicationServerLLGS::
+    Handle_jAcceleratorPluginGetDynamicLoaderLibraryInfo(
+        StringExtractorGDBRemote &packet) {
+  packet.ConsumeFront("jAcceleratorPluginGetDynamicLoaderLibraryInfo:");
+  llvm::Expected<AcceleratorDynamicLoaderArgs> args =
+      llvm::json::parse<AcceleratorDynamicLoaderArgs>(
+          packet.Peek(), "AcceleratorDynamicLoaderArgs");
+  if (!args)
+    return SendErrorResponse(args.takeError());
+
+  for (std::unique_ptr<lldb_server::LLDBServerAcceleratorPlugin> &plugin_up :
+       m_accelerator_plugins) {
+    if (plugin_up->GetPluginName() == args->plugin_name) {
+      std::optional<AcceleratorDynamicLoaderResponse> response =
+          plugin_up->GetDynamicLoaderLibraryInfos(*args);
+      if (response) {
+        StreamGDBRemote stream;
+        stream.PutAsJSON(*response, /*hex_ascii=*/false);
+        return SendPacketNoLock(stream.GetString());
+      }
+      return SendErrorResponse(
+          Status::FromErrorString("no dynamic loader info available"));
+    }
+  }
+  return SendErrorResponse(
+      Status::FromErrorString("unknown accelerator plugin name"));
+}
diff --git 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index e5b4c9ec0bed0..3c8afe699bbaf 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -302,6 +302,9 @@ class GDBRemoteCommunicationServerLLGS
   PacketResult
   Handle_jAcceleratorPluginBreakpointHit(StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_jAcceleratorPluginGetDynamicLoaderLibraryInfo(
+      StringExtractorGDBRemote &packet);
+
   void SetCurrentThreadID(lldb::tid_t tid);
 
   lldb::tid_t GetCurrentThreadID() const;
diff --git 
a/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h 
b/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
index f16ee82a39d95..2e585d9a64c6a 100644
--- a/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
+++ b/lldb/source/Plugins/Process/gdb-remote/LLDBServerAcceleratorPlugin.h
@@ -47,6 +47,11 @@ class LLDBServerAcceleratorPlugin {
                               ++m_accelerator_action_identifier);
   }
 
+  virtual std::optional<AcceleratorDynamicLoaderResponse>
+  GetDynamicLoaderLibraryInfos(const AcceleratorDynamicLoaderArgs &args) {
+    return std::nullopt;
+  }
+
 protected:
   GDBServer &m_native_gdb_server;
   MainLoop &m_native_main_loop;
diff --git a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp 
b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
index 3b9edada64c65..169e3ac8017b5 100644
--- a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
+++ b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
@@ -13,7 +13,7 @@ using namespace llvm::json;
 
 namespace lldb_private {
 
-bool fromJSON(const Value &value, SymbolValue &data, Path path) {
+bool fromJSON(const json::Value &value, SymbolValue &data, Path path) {
   ObjectMapper o(value, path);
   return o && o.map("name", data.name) && o.map("value", data.value);
 }
@@ -22,7 +22,7 @@ json::Value toJSON(const SymbolValue &data) {
   return Object{{"name", data.name}, {"value", data.value}};
 }
 
-bool fromJSON(const Value &value, AcceleratorBreakpointByName &data,
+bool fromJSON(const json::Value &value, AcceleratorBreakpointByName &data,
               Path path) {
   ObjectMapper o(value, path);
   return o && o.mapOptional("shlib", data.shlib) &&
@@ -33,7 +33,7 @@ json::Value toJSON(const AcceleratorBreakpointByName &data) {
   return Object{{"shlib", data.shlib}, {"function_name", data.function_name}};
 }
 
-bool fromJSON(const Value &value, AcceleratorBreakpointByAddress &data,
+bool fromJSON(const json::Value &value, AcceleratorBreakpointByAddress &data,
               Path path) {
   ObjectMapper o(value, path);
   return o && o.map("load_address", data.load_address);
@@ -43,7 +43,8 @@ json::Value toJSON(const AcceleratorBreakpointByAddress 
&data) {
   return Object{{"load_address", static_cast<int64_t>(data.load_address)}};
 }
 
-bool fromJSON(const Value &value, AcceleratorBreakpointInfo &data, Path path) {
+bool fromJSON(const json::Value &value, AcceleratorBreakpointInfo &data,
+              Path path) {
   ObjectMapper o(value, path);
   return o && o.map("identifier", data.identifier) &&
          o.mapOptional("by_name", data.by_name) &&
@@ -60,7 +61,7 @@ json::Value toJSON(const AcceleratorBreakpointInfo &data) {
   };
 }
 
-bool fromJSON(const Value &value, AcceleratorBreakpointHitArgs &data,
+bool fromJSON(const json::Value &value, AcceleratorBreakpointHitArgs &data,
               Path path) {
   ObjectMapper o(value, path);
   return o && o.map("plugin_name", data.plugin_name) &&
@@ -76,7 +77,7 @@ json::Value toJSON(const AcceleratorBreakpointHitArgs &data) {
   };
 }
 
-std::optional<uint64_t>
+std::optional<lldb::addr_t>
 AcceleratorBreakpointHitArgs::GetSymbolValue(StringRef symbol_name) const {
   auto it = llvm::find_if(symbol_values, [&](const SymbolValue &symbol) {
     return symbol.name == symbol_name;
@@ -86,7 +87,8 @@ AcceleratorBreakpointHitArgs::GetSymbolValue(StringRef 
symbol_name) const {
   return std::nullopt;
 }
 
-bool fromJSON(const Value &value, AcceleratorConnectionInfo &data, Path path) {
+bool fromJSON(const json::Value &value, AcceleratorConnectionInfo &data,
+              Path path) {
   ObjectMapper o(value, path);
   return o && o.map("connect_url", data.connect_url) &&
          o.map("platform_name", data.platform_name) &&
@@ -103,7 +105,7 @@ json::Value toJSON(const AcceleratorConnectionInfo &data) {
   };
 }
 
-bool fromJSON(const Value &value, AcceleratorActions &data, Path path) {
+bool fromJSON(const json::Value &value, AcceleratorActions &data, Path path) {
   ObjectMapper o(value, path);
   return o && o.map("plugin_name", data.plugin_name) &&
          o.map("session_name", data.session_name) &&
@@ -124,7 +126,7 @@ json::Value toJSON(const AcceleratorActions &data) {
   return obj;
 }
 
-bool fromJSON(const Value &value, AcceleratorBreakpointHitResponse &data,
+bool fromJSON(const json::Value &value, AcceleratorBreakpointHitResponse &data,
               Path path) {
   ObjectMapper o(value, path);
   return o && o.map("disable_bp", data.disable_bp) &&
@@ -142,4 +144,64 @@ json::Value toJSON(const AcceleratorBreakpointHitResponse 
&data) {
   return obj;
 }
 
+bool fromJSON(const json::Value &value, AcceleratorSectionInfo &data,
+              Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("names", data.names) &&
+         o.map("load_address", data.load_address);
+}
+
+json::Value toJSON(const AcceleratorSectionInfo &data) {
+  return Object{{"names", data.names},
+                {"load_address", static_cast<int64_t>(data.load_address)}};
+}
+
+bool fromJSON(const json::Value &value,
+              AcceleratorDynamicLoaderLibraryInfo &data, Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("pathname", data.pathname) &&
+         o.mapOptional("uuid", data.uuid_str) && o.map("load", data.load) &&
+         o.mapOptional("load_address", data.load_address) &&
+         o.mapOptional("loaded_sections", data.loaded_sections) &&
+         o.mapOptional("native_memory_address", data.native_memory_address) &&
+         o.mapOptional("native_memory_size", data.native_memory_size) &&
+         o.mapOptional("file_offset", data.file_offset) &&
+         o.mapOptional("file_size", data.file_size);
+}
+
+json::Value toJSON(const AcceleratorDynamicLoaderLibraryInfo &data) {
+  return Object{
+      {"pathname", data.pathname},
+      {"uuid", data.uuid_str},
+      {"load", data.load},
+      {"load_address", data.load_address},
+      {"loaded_sections", data.loaded_sections},
+      {"native_memory_address", data.native_memory_address},
+      {"native_memory_size", data.native_memory_size},
+      {"file_offset", data.file_offset},
+      {"file_size", data.file_size},
+  };
+}
+
+bool fromJSON(const json::Value &value, AcceleratorDynamicLoaderArgs &data,
+              Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("plugin_name", data.plugin_name) &&
+         o.map("full", data.full);
+}
+
+json::Value toJSON(const AcceleratorDynamicLoaderArgs &data) {
+  return Object{{"plugin_name", data.plugin_name}, {"full", data.full}};
+}
+
+bool fromJSON(const json::Value &value, AcceleratorDynamicLoaderResponse &data,
+              Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("library_infos", data.library_infos);
+}
+
+json::Value toJSON(const AcceleratorDynamicLoaderResponse &data) {
+  return Object{{"library_infos", data.library_infos}};
+}
+
 } // namespace lldb_private
diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp 
b/lldb/source/Utility/StringExtractorGDBRemote.cpp
index 6fc3b63e02dd1..953a2cd3090d1 100644
--- a/lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -338,6 +338,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
       return eServerPacketType_jAcceleratorPluginInitialize;
     if (PACKET_STARTS_WITH("jAcceleratorPluginBreakpointHit:"))
       return eServerPacketType_jAcceleratorPluginBreakpointHit;
+    if (PACKET_STARTS_WITH("jAcceleratorPluginGetDynamicLoaderLibraryInfo:"))
+      return eServerPacketType_jAcceleratorPluginGetDynamicLoaderLibraryInfo;
     break;
 
   case 'v':
diff --git a/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py 
b/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py
index 941e0c97a8f2e..e40e6491e59a0 100644
--- a/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py
+++ b/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py
@@ -186,3 +186,58 @@ def 
test_jAcceleratorPluginBreakpointHit_returns_connect_info(self):
             connect_info["connect_url"],
         )
         self.assertTrue(connect_info["synchronous"])
+
+    @add_test_categories(["llgs"])
+    def test_jAcceleratorPluginGetDynamicLoaderLibraryInfo(self):
+        self.build()
+        self.set_inferior_startup_launch()
+        self.prep_debug_monitor_and_inferior()
+
+        self.add_qSupported_packets()
+        self.expect_gdbremote_sequence()
+
+        # Only "full" is exercised. The mock could answer full=False, but what
+        # a delta contains is entirely up to the plugin, so doing so would only
+        # test the mock's own bookkeeping. That belongs with a real plugin.
+        dyld_args = {"plugin_name": "mock", "full": True}
+        dyld_json = json.dumps(dyld_args, separators=(",", ":"))
+        escaped_json = escape_binary(dyld_json)
+        response = self.send_and_decode_json(
+            "jAcceleratorPluginGetDynamicLoaderLibraryInfo:" + escaped_json
+        )
+
+        self.assertIn("library_infos", response)
+        libs = response["library_infos"]
+        self.assertGreater(len(libs), 0)
+
+        lib = libs[0]
+        self.assertEqual(lib["pathname"], "/path/to/lib.so")
+        self.assertTrue(lib["load"])
+        self.assertEqual(lib["load_address"], 0x7F000000)
+
+    @add_test_categories(["llgs"])
+    def 
test_jAcceleratorPluginGetDynamicLoaderLibraryInfo_unknown_plugin(self):
+        self.build()
+        self.set_inferior_startup_launch()
+        self.prep_debug_monitor_and_inferior()
+
+        self.add_qSupported_packets()
+        self.expect_gdbremote_sequence()
+
+        dyld_args = {"plugin_name": "no-such-plugin", "full": True}
+        dyld_json = json.dumps(dyld_args, separators=(",", ":"))
+        escaped_json = escape_binary(dyld_json)
+        self.test_sequence.add_log_lines(
+            [
+                "read packet: 
$jAcceleratorPluginGetDynamicLoaderLibraryInfo:%s#00"
+                % escaped_json,
+                {
+                    "direction": "send",
+                    "regex": r"^\$(E[0-9a-fA-F]{2}.*)#[0-9a-fA-F]{2}",
+                    "capture": {1: "error"},
+                },
+            ],
+            True,
+        )
+        context = self.expect_gdbremote_sequence()
+        self.assertIsNotNone(context.get("error"))
diff --git 
a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
 
b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
index 6c1d23fef8a71..0cce2955f6e0d 100644
--- 
a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
+++ 
b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
@@ -220,3 +220,15 @@ LLDBServerMockAcceleratorPlugin::CreateConnection() {
   info.synchronous = true;
   return info;
 }
+
+std::optional<AcceleratorDynamicLoaderResponse>
+LLDBServerMockAcceleratorPlugin::GetDynamicLoaderLibraryInfos(
+    const AcceleratorDynamicLoaderArgs &args) {
+  AcceleratorDynamicLoaderResponse response;
+  AcceleratorDynamicLoaderLibraryInfo lib;
+  lib.pathname = "/path/to/lib.so";
+  lib.load = true;
+  lib.load_address = 0x7f000000;
+  response.library_infos.push_back(std::move(lib));
+  return response;
+}
diff --git 
a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
 
b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
index b07266d0ef65f..36f6aa2500ba8 100644
--- 
a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
+++ 
b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.h
@@ -34,6 +34,9 @@ class LLDBServerMockAcceleratorPlugin : public 
LLDBServerAcceleratorPlugin {
   llvm::Expected<AcceleratorBreakpointHitResponse>
   BreakpointWasHit(AcceleratorBreakpointHitArgs &args) override;
 
+  std::optional<AcceleratorDynamicLoaderResponse> GetDynamicLoaderLibraryInfos(
+      const AcceleratorDynamicLoaderArgs &args) override;
+
 private:
   // Lazily bring up the mock accelerator GDB server and return its connection
   // info. Called on the connection breakpoint hit, so inferiors that never
diff --git a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp 
b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
index 571dd2cab124a..8d64b4a8acf78 100644
--- a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
+++ b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
@@ -244,3 +244,84 @@ TEST(AcceleratorGDBRemotePacketsTest, 
AcceleratorActionsWithoutConnectInfo) {
   ASSERT_THAT_EXPECTED(deserialized, Succeeded());
   EXPECT_FALSE(deserialized->connect_info.has_value());
 }
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorSectionInfo) {
+  AcceleratorSectionInfo section;
+  section.names = {"PT_LOAD[0]", ".text"};
+  section.load_address = 0x400000;
+
+  Expected<AcceleratorSectionInfo> deserialized = roundtripJSON(section);
+  ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+  EXPECT_EQ(section.names, deserialized->names);
+  EXPECT_EQ(section.load_address, deserialized->load_address);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorDynamicLoaderLibraryInfo) {
+  AcceleratorDynamicLoaderLibraryInfo lib;
+  lib.pathname = "/usr/lib/libgpu.so";
+  lib.uuid_str = "AABBCCDD";
+  lib.load = true;
+  lib.load_address = 0x7f000000;
+  lib.native_memory_address = 0x1000;
+  lib.native_memory_size = 0x2000;
+  lib.file_offset = 4096;
+  lib.file_size = 8192;
+
+  AcceleratorSectionInfo section;
+  section.names = {".text"};
+  section.load_address = 0x7f001000;
+  lib.loaded_sections.push_back(std::move(section));
+
+  Expected<AcceleratorDynamicLoaderLibraryInfo> deserialized =
+      roundtripJSON(lib);
+  ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+  EXPECT_EQ(lib.pathname, deserialized->pathname);
+  EXPECT_EQ(lib.uuid_str, deserialized->uuid_str);
+  EXPECT_EQ(lib.load, deserialized->load);
+  EXPECT_EQ(lib.load_address, deserialized->load_address);
+  EXPECT_EQ(lib.native_memory_address, deserialized->native_memory_address);
+  EXPECT_EQ(lib.native_memory_size, deserialized->native_memory_size);
+  EXPECT_EQ(lib.file_offset, deserialized->file_offset);
+  EXPECT_EQ(lib.file_size, deserialized->file_size);
+  ASSERT_EQ(1u, deserialized->loaded_sections.size());
+  EXPECT_EQ(0x7f001000u, deserialized->loaded_sections[0].load_address);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorDynamicLoaderArgs) {
+  AcceleratorDynamicLoaderArgs args;
+  args.plugin_name = "mock";
+  args.full = true;
+
+  Expected<AcceleratorDynamicLoaderArgs> deserialized = roundtripJSON(args);
+  ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+  EXPECT_EQ(args.plugin_name, deserialized->plugin_name);
+  EXPECT_EQ(args.full, deserialized->full);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, AcceleratorDynamicLoaderResponse) {
+  AcceleratorDynamicLoaderResponse response;
+  AcceleratorDynamicLoaderLibraryInfo lib;
+  lib.pathname = "/path/to/lib.so";
+  lib.load = true;
+  response.library_infos.push_back(std::move(lib));
+
+  Expected<AcceleratorDynamicLoaderResponse> deserialized =
+      roundtripJSON(response);
+  ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+  ASSERT_EQ(1u, deserialized->library_infos.size());
+  EXPECT_EQ("/path/to/lib.so", deserialized->library_infos[0].pathname);
+  EXPECT_TRUE(deserialized->library_infos[0].load);
+}
+
+TEST(AcceleratorGDBRemotePacketsTest, DynamicLoaderLibraryInfoOptionalFields) {
+  // A server with no per-section addresses can omit "loaded_sections".
+  Expected<AcceleratorDynamicLoaderLibraryInfo> deserialized =
+      json::parse<AcceleratorDynamicLoaderLibraryInfo>(
+          R"({"pathname":"/path/to/lib.so","load":true})",
+          "AcceleratorDynamicLoaderLibraryInfo");
+  ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+  EXPECT_EQ("/path/to/lib.so", deserialized->pathname);
+  EXPECT_TRUE(deserialized->load);
+  EXPECT_TRUE(deserialized->loaded_sections.empty());
+  EXPECT_FALSE(deserialized->load_address.has_value());
+}

>From 40183939353b57bc84b12dc44a756f7b19f5e337 Mon Sep 17 00:00:00 2001
From: satya janga <[email protected]>
Date: Sun, 5 Jul 2026 08:01:36 -0700
Subject: [PATCH 2/2] [lldb] Add accelerator dynamic loader over gdb-remote

Accelerator (e.g. GPU) targets do not use a rendezvous breakpoint the way
SVR4 loaders do; their runtime tells the lldb-server accelerator plugin when
libraries load or unload. This adds a DynamicLoader that gets that list from
the server and loads the modules into the target.

The loader is selected for accelerator architectures debugged over gdb-remote:
CreateInstance claims AMDGPU and NVPTX triples backed by a ProcessGDBRemote.
It then asks for libraries via jAcceleratorPluginGetDynamicLoaderLibraryInfo
and loads each, honoring a whole-file path or a slice of a containing file
located by file offset/size.

The mock accelerator process implements the new NativeProcessProtocol hook so
the same packet is served on an accelerator connection. Tests use a mock GDB
server reporting an AMDGPU target, covering both library-provision paths and
that a host target does not select this loader.
---
 .../lldb/Host/common/NativeProcessProtocol.h  |   9 +
 .../AcceleratorGDBRemote/CMakeLists.txt       |  13 ++
 .../DynamicLoaderAcceleratorGDBRemote.cpp     | 156 ++++++++++++++++++
 .../DynamicLoaderAcceleratorGDBRemote.h       |  46 ++++++
 .../Plugins/DynamicLoader/CMakeLists.txt      |   1 +
 .../GDBRemoteCommunicationClient.cpp          |  23 +++
 .../gdb-remote/GDBRemoteCommunicationClient.h |   5 +
 .../GDBRemoteCommunicationServerLLGS.cpp      |  41 +++--
 .../TestAcceleratorDynamicLoader.py           | 153 +++++++++++++++++
 .../dynamic_loader/accelerator.yaml           |  21 +++
 .../dynamic_loader/accelerator_lib.yaml       |  21 +++
 .../dynamic_loader/embedded_lib.yaml          |  21 +++
 .../API/accelerator/dynamic_loader/host.yaml  |  20 +++
 .../Mock/ProcessMockAccelerator.cpp           |  12 ++
 .../Accelerator/Mock/ProcessMockAccelerator.h |   4 +
 15 files changed, 533 insertions(+), 13 deletions(-)
 create mode 100644 
lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/CMakeLists.txt
 create mode 100644 
lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.cpp
 create mode 100644 
lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.h
 create mode 100644 
lldb/test/API/accelerator/dynamic_loader/TestAcceleratorDynamicLoader.py
 create mode 100644 lldb/test/API/accelerator/dynamic_loader/accelerator.yaml
 create mode 100644 
lldb/test/API/accelerator/dynamic_loader/accelerator_lib.yaml
 create mode 100644 lldb/test/API/accelerator/dynamic_loader/embedded_lib.yaml
 create mode 100644 lldb/test/API/accelerator/dynamic_loader/host.yaml

diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h 
b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 67206c4b55b79..0befe0d211fa7 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/AcceleratorGDBRemotePackets.h"
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/Iterable.h"
 #include "lldb/Utility/ProcessAddress.h"
@@ -163,6 +164,14 @@ class NativeProcessProtocol {
                                    "Not implemented");
   }
 
+  /// Answers "jAcceleratorPluginGetDynamicLoaderLibraryInfo" on an accelerator
+  /// connection. std::nullopt if this process does not provide libraries.
+  virtual std::optional<AcceleratorDynamicLoaderResponse>
+  GetAcceleratorDynamicLoaderLibraryInfos(
+      const AcceleratorDynamicLoaderArgs &args) {
+    return std::nullopt;
+  }
+
   virtual bool HasPendingLibraryEvents() { return false; }
 
   virtual bool IsAlive() const;
diff --git 
a/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/CMakeLists.txt 
b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/CMakeLists.txt
new file mode 100644
index 0000000000000..b5cf71d1f8b31
--- /dev/null
+++ b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_lldb_library(lldbPluginDynamicLoaderAcceleratorGDBRemote PLUGIN
+  DynamicLoaderAcceleratorGDBRemote.cpp
+
+  LINK_LIBS
+    lldbCore
+    lldbHost
+    lldbSymbol
+    lldbTarget
+    lldbUtility
+    lldbPluginProcessGDBRemote
+  LINK_COMPONENTS
+    Support
+  )
diff --git 
a/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.cpp
 
b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.cpp
new file mode 100644
index 0000000000000..9c6a391525a08
--- /dev/null
+++ 
b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.cpp
@@ -0,0 +1,156 @@
+//===-- DynamicLoaderAcceleratorGDBRemote.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 "DynamicLoaderAcceleratorGDBRemote.h"
+#include "Plugins/Process/gdb-remote/ProcessGDBRemote.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Core/Section.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+
+using namespace lldb;
+using namespace lldb_private;
+using namespace lldb_private::process_gdb_remote;
+
+LLDB_PLUGIN_DEFINE(DynamicLoaderAcceleratorGDBRemote)
+
+DynamicLoader *
+DynamicLoaderAcceleratorGDBRemote::CreateInstance(Process *process,
+                                                  bool force) {
+  // The library list comes from the accelerator's GDB server.
+  if (process->GetPluginName() != ProcessGDBRemote::GetPluginNameStatic())
+    return nullptr;
+  if (force)
+    return new DynamicLoaderAcceleratorGDBRemote(process);
+
+  const llvm::Triple &triple =
+      process->GetTarget().GetArchitecture().GetTriple();
+  if (triple.isAMDGPU() || triple.isNVPTX())
+    return new DynamicLoaderAcceleratorGDBRemote(process);
+  return nullptr;
+}
+
+DynamicLoaderAcceleratorGDBRemote::DynamicLoaderAcceleratorGDBRemote(
+    Process *process)
+    : DynamicLoader(process) {}
+
+void DynamicLoaderAcceleratorGDBRemote::DidAttach() {
+  LoadModulesFromGDBServer(/*full=*/true);
+}
+
+void DynamicLoaderAcceleratorGDBRemote::DidLaunch() {
+  LoadModulesFromGDBServer(/*full=*/true);
+}
+
+bool DynamicLoaderAcceleratorGDBRemote::LoadModulesFromGDBServer(bool full) {
+  Log *log = GetLog(LLDBLog::DynamicLoader);
+
+  // Safe: CreateInstance only builds this loader for a ProcessGDBRemote.
+  ProcessGDBRemote *gdb_process = static_cast<ProcessGDBRemote *>(m_process);
+  AcceleratorDynamicLoaderArgs args;
+  args.full = full;
+
+  Target &target = m_process->GetTarget();
+  ModuleList loaded_module_list;
+  std::optional<AcceleratorDynamicLoaderResponse> response =
+      
gdb_process->GetGDBRemote().GetAcceleratorDynamicLoaderLibraryInfos(args);
+  if (!response) {
+    LLDB_LOG(log, "failed to get dynamic loader info from the GDB server");
+    return false;
+  }
+
+  for (const AcceleratorDynamicLoaderLibraryInfo &info :
+       response->library_infos) {
+    UUID uuid;
+    if (info.uuid_str)
+      uuid.SetFromStringRef(*info.uuid_str);
+
+    // Either a whole file, or a slice of a containing file.
+    ModuleSpec module_spec(FileSpec(info.pathname), uuid);
+    if (info.file_offset)
+      module_spec.SetObjectOffset(*info.file_offset);
+    if (info.file_size)
+      module_spec.SetObjectSize(*info.file_size);
+
+    if (!info.load) {
+      ModuleList matching_module_list;
+      target.GetImages().FindModules(module_spec, matching_module_list);
+      matching_module_list.ForEach(
+          [this](const ModuleSP &module_sp) -> IterationAction {
+            UnloadSections(module_sp);
+            return IterationAction::Continue;
+          });
+      continue;
+    }
+
+    ModuleSP module_sp = target.GetOrCreateModule(module_spec, 
/*notify=*/true);
+    if (!module_sp)
+      continue;
+
+    bool changed = false;
+    if (info.load_address) {
+      module_sp->SetLoadAddress(target, *info.load_address,
+                                /*value_is_offset=*/true, changed);
+    } else if (!info.loaded_sections.empty()) {
+      for (const AcceleratorSectionInfo &sect : info.loaded_sections) {
+        if (sect.names.empty())
+          continue;
+        SectionSP section_sp;
+        for (const std::string &name : sect.names) {
+          ConstString section_name(name);
+          if (section_sp)
+            section_sp =
+                section_sp->GetChildren().FindSectionByName(section_name);
+          else
+            section_sp =
+                module_sp->GetSectionList()->FindSectionByName(section_name);
+          if (!section_sp)
+            break;
+        }
+        if (section_sp)
+          changed |= target.SetSectionLoadAddress(section_sp, 
sect.load_address,
+                                                  /*warn_multiple=*/true);
+      }
+    } else {
+      // No slide: load at the file addresses.
+      module_sp->SetLoadAddress(target, 0, /*value_is_offset=*/true, changed);
+    }
+
+    if (changed)
+      loaded_module_list.AppendIfNeeded(module_sp);
+  }
+
+  target.ModulesDidLoad(loaded_module_list);
+  return true;
+}
+
+ThreadPlanSP DynamicLoaderAcceleratorGDBRemote::GetStepThroughTrampolinePlan(
+    Thread &thread, bool stop_others) {
+  return ThreadPlanSP();
+}
+
+Status DynamicLoaderAcceleratorGDBRemote::CanLoadImage() {
+  return Status::FromErrorString("can't load images on accelerator targets");
+}
+
+void DynamicLoaderAcceleratorGDBRemote::Initialize() {
+  PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                GetPluginDescriptionStatic(), CreateInstance);
+}
+
+void DynamicLoaderAcceleratorGDBRemote::Terminate() {
+  PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+llvm::StringRef
+DynamicLoaderAcceleratorGDBRemote::GetPluginDescriptionStatic() {
+  return "Dynamic loader plug-in that gets shared library loads/unloads from "
+         "an lldb-server accelerator plugin.";
+}
diff --git 
a/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.h
 
b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.h
new file mode 100644
index 0000000000000..1af218b7acd8c
--- /dev/null
+++ 
b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.h
@@ -0,0 +1,46 @@
+//===-- DynamicLoaderAcceleratorGDBRemote.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_SOURCE_PLUGINS_DYNAMICLOADER_ACCELERATORGDBREMOTE_DYNAMICLOADERACCELERATORGDBREMOTE_H
+#define 
LLDB_SOURCE_PLUGINS_DYNAMICLOADER_ACCELERATORGDBREMOTE_DYNAMICLOADERACCELERATORGDBREMOTE_H
+
+#include "lldb/Target/DynamicLoader.h"
+
+/// Dynamic loader for accelerator (e.g. GPU) targets.
+///
+/// Accelerators don't set a rendezvous breakpoint the way SVR4 loaders do;
+/// their runtime tells the lldb-server plugin when libraries load or unload.
+/// This loader asks the server for that list via
+/// "jAcceleratorPluginGetDynamicLoaderLibraryInfo".
+class DynamicLoaderAcceleratorGDBRemote : public lldb_private::DynamicLoader {
+public:
+  DynamicLoaderAcceleratorGDBRemote(lldb_private::Process *process);
+
+  static void Initialize();
+  static void Terminate();
+  static llvm::StringRef GetPluginNameStatic() {
+    return "accelerator-gdb-remote";
+  }
+  static llvm::StringRef GetPluginDescriptionStatic();
+  static lldb_private::DynamicLoader *
+  CreateInstance(lldb_private::Process *process, bool force);
+
+  void DidAttach() override;
+  void DidLaunch() override;
+  lldb::ThreadPlanSP GetStepThroughTrampolinePlan(lldb_private::Thread &thread,
+                                                  bool stop_others) override;
+  lldb_private::Status CanLoadImage() override;
+
+  llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+private:
+  /// Returns true if the server answered the packet.
+  bool LoadModulesFromGDBServer(bool full);
+};
+
+#endif // 
LLDB_SOURCE_PLUGINS_DYNAMICLOADER_ACCELERATORGDBREMOTE_DYNAMICLOADERACCELERATORGDBREMOTE_H
diff --git a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt 
b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt
index 01aba34b94169..97604bb4824db 100644
--- a/lldb/source/Plugins/DynamicLoader/CMakeLists.txt
+++ b/lldb/source/Plugins/DynamicLoader/CMakeLists.txt
@@ -5,6 +5,7 @@ set_property(DIRECTORY PROPERTY 
LLDB_TOLERATED_PLUGIN_DEPENDENCIES
   TypeSystem
 )
 
+add_subdirectory(AcceleratorGDBRemote)
 add_subdirectory(Darwin-Kernel)
 add_subdirectory(FreeBSD-Kernel)
 add_subdirectory(MacOSX-DYLD)
diff --git 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
index b440869f25984..4dc14d8ff8a4f 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -300,6 +300,29 @@ GDBRemoteCommunicationClient::AcceleratorBreakpointHit(
       response.GetStringRef(), llvm::toString(hit_response.takeError()));
 }
 
+std::optional<AcceleratorDynamicLoaderResponse>
+GDBRemoteCommunicationClient::GetAcceleratorDynamicLoaderLibraryInfos(
+    const AcceleratorDynamicLoaderArgs &args) {
+  StreamGDBRemote packet;
+  packet.PutCString("jAcceleratorPluginGetDynamicLoaderLibraryInfo:");
+  packet.PutAsJSON(args, /*hex_ascii=*/false);
+
+  StringExtractorGDBRemote response;
+  if (SendPacketAndWaitForResponse(packet.GetString(), response) !=
+          PacketResult::Success ||
+      response.IsErrorResponse())
+    return std::nullopt;
+
+  llvm::Expected<AcceleratorDynamicLoaderResponse> parsed =
+      llvm::json::parse<AcceleratorDynamicLoaderResponse>(
+          response.Peek(), "AcceleratorDynamicLoaderResponse");
+  if (!parsed) {
+    llvm::consumeError(parsed.takeError());
+    return std::nullopt;
+  }
+  return *parsed;
+}
+
 bool GDBRemoteCommunicationClient::QueryNoAckModeSupported() {
   if (m_supports_not_sending_acks == eLazyBoolCalculate) {
     m_send_acks = true;
diff --git 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
index 3a0a34f840c21..9f946f030c4ff 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -377,6 +377,11 @@ class GDBRemoteCommunicationClient : public 
GDBRemoteClientBase {
   llvm::Expected<AcceleratorBreakpointHitResponse>
   AcceleratorBreakpointHit(const AcceleratorBreakpointHitArgs &args);
 
+  /// Returns std::nullopt if the packet failed or the response did not parse.
+  std::optional<AcceleratorDynamicLoaderResponse>
+  GetAcceleratorDynamicLoaderLibraryInfos(
+      const AcceleratorDynamicLoaderArgs &args);
+
   LazyBool SupportsAllocDeallocMemory() // const
   {
     // Uncomment this to have lldb pretend the debug server doesn't respond to
diff --git 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 791c02fd96b20..159f423ca0d66 100644
--- 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -4659,20 +4659,35 @@ GDBRemoteCommunication::PacketResult 
GDBRemoteCommunicationServerLLGS::
   if (!args)
     return SendErrorResponse(args.takeError());
 
-  for (std::unique_ptr<lldb_server::LLDBServerAcceleratorPlugin> &plugin_up :
-       m_accelerator_plugins) {
-    if (plugin_up->GetPluginName() == args->plugin_name) {
-      std::optional<AcceleratorDynamicLoaderResponse> response =
-          plugin_up->GetDynamicLoaderLibraryInfos(*args);
-      if (response) {
-        StreamGDBRemote stream;
-        stream.PutAsJSON(*response, /*hex_ascii=*/false);
-        return SendPacketNoLock(stream.GetString());
+  // On the native connection, forward to the named accelerator plugin.
+  if (!m_accelerator_plugins.empty()) {
+    for (std::unique_ptr<lldb_server::LLDBServerAcceleratorPlugin> &plugin_up :
+         m_accelerator_plugins) {
+      if (plugin_up->GetPluginName() == args->plugin_name) {
+        std::optional<AcceleratorDynamicLoaderResponse> response =
+            plugin_up->GetDynamicLoaderLibraryInfos(*args);
+        if (response) {
+          StreamGDBRemote stream;
+          stream.PutAsJSON(*response, /*hex_ascii=*/false);
+          return SendPacketNoLock(stream.GetString());
+        }
+        return SendErrorResponse(
+            Status::FromErrorString("no dynamic loader info available"));
       }
-      return SendErrorResponse(
-          Status::FromErrorString("no dynamic loader info available"));
     }
+    return SendErrorResponse(
+        Status::FromErrorString("unknown accelerator plugin name"));
   }
-  return SendErrorResponse(
-      Status::FromErrorString("unknown accelerator plugin name"));
+
+  // On the accelerator connection, ask the process directly.
+  if (!m_current_process)
+    return SendErrorResponse(Status::FromErrorString("no current process"));
+  std::optional<AcceleratorDynamicLoaderResponse> response =
+      m_current_process->GetAcceleratorDynamicLoaderLibraryInfos(*args);
+  if (!response)
+    return SendErrorResponse(
+        Status::FromErrorString("dynamic loader library info not supported"));
+  StreamGDBRemote stream;
+  stream.PutAsJSON(*response, /*hex_ascii=*/false);
+  return SendPacketNoLock(stream.GetString());
 }
diff --git 
a/lldb/test/API/accelerator/dynamic_loader/TestAcceleratorDynamicLoader.py 
b/lldb/test/API/accelerator/dynamic_loader/TestAcceleratorDynamicLoader.py
new file mode 100644
index 0000000000000..30740642fbd07
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/TestAcceleratorDynamicLoader.py
@@ -0,0 +1,153 @@
+"""
+Test the accelerator dynamic loader against a mock GDB server.
+
+The loader is selected for accelerator architectures debugged over gdb-remote.
+It then asks the server for the loaded libraries via
+jAcceleratorPluginGetDynamicLoaderLibraryInfo and loads them into the target.
+"""
+
+import json
+import os
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.gdbclientutils import *
+from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
+
+DYLD_PACKET = "jAcceleratorPluginGetDynamicLoaderLibraryInfo:"
+
+
+class AcceleratorResponder(MockGDBServerResponder):
+    """Serves a fixed set of library infos, and counts how often it is 
asked."""
+
+    def __init__(self, library_infos):
+        MockGDBServerResponder.__init__(self)
+        self.library_infos = library_infos
+        self.dyld_queries = 0
+
+    def qSupported(self, client_supported):
+        return super().qSupported(client_supported) + ";qXfer:features:read+"
+
+    def qXferRead(self, obj, annex, offset, length):
+        # An accelerator architecture has no built-in register set in lldb.
+        if obj == "features" and annex == "target.xml":
+            return (
+                """<?xml version="1.0"?>
+                <target version="1.0">
+                  <feature name="org.llvm.accelerator">
+                    <reg name="pc" bitsize="64" regnum="0" type="code_ptr" 
group="general"/>
+                  </feature>
+                </target>""",
+                False,
+            )
+        return None, False
+
+    def readRegisters(self):
+        return "00" * 8
+
+    def other(self, packet):
+        if packet.startswith(DYLD_PACKET):
+            self.dyld_queries += 1
+            # "}" is the gdb-remote escape character.
+            return escape_binary(
+                json.dumps({"library_infos": self.library_infos}, 
separators=(",", ":"))
+            )
+        return ""
+
+
+class TestAcceleratorDynamicLoader(GDBRemoteTestBase):
+    def make_library(self):
+        """Build the library object file the server will report."""
+        path = self.getBuildArtifact("accelerator_lib.so")
+        self.yaml2obj("accelerator_lib.yaml", path)
+        return path
+
+    def make_container(self, outer_path):
+        """Embed a library in a larger file, as llvm-objcopy would when adding
+        it to a container. Returns the container path and the (offset, size) of
+        the embedded object.
+
+        The embedded object puts .text at a different address than the outer
+        one, so the assertions fail if the slice is ignored and the container 
is
+        parsed from offset 0."""
+        embedded_path = self.getBuildArtifact("embedded_lib.so")
+        self.yaml2obj("embedded_lib.yaml", embedded_path)
+        with open(outer_path, "rb") as f:
+            outer_bytes = f.read()
+        with open(embedded_path, "rb") as f:
+            embedded_bytes = f.read()
+        # The container must itself be a valid object file.
+        prefix = outer_bytes + b"\x00" * ((-len(outer_bytes)) % 0x1000)
+        container_path = self.getBuildArtifact("container.bin")
+        with open(container_path, "wb") as f:
+            f.write(prefix)
+            f.write(embedded_bytes)
+        return container_path, len(prefix), len(embedded_bytes)
+
+    def find_module(self, target, path):
+        basename = os.path.basename(path)
+        for i in range(target.GetNumModules()):
+            module = target.GetModuleAtIndex(i)
+            if module.GetFileSpec().GetFilename() == basename:
+                return module
+        return None
+
+    def connect_accelerator(self, library_infos):
+        self.server.responder = AcceleratorResponder(library_infos)
+        target = self.createTarget("accelerator.yaml")
+        process = self.connect(target)
+        self.assertTrue(process.IsValid(), "Process is valid")
+        return target
+
+    def assert_text_loaded_at(self, target, module, expected):
+        section = module.FindSection(".text")
+        self.assertTrue(section.IsValid(), "library should have a .text 
section")
+        self.assertEqual(section.GetLoadAddress(target), expected)
+
+    def test_whole_file_library(self):
+        """A library given as a whole file is loaded at the reported 
address."""
+        lib = self.make_library()
+        target = self.connect_accelerator(
+            [{"pathname": lib, "load": True, "load_address": 0x10000000}]
+        )
+
+        module = self.find_module(target, lib)
+        self.assertIsNotNone(module, "library should be loaded into the 
target")
+        # load_address slides the file, so .text (file address 0x1000) lands
+        # 0x1000 past the base.
+        self.assert_text_loaded_at(target, module, 0x10001000)
+
+    def test_library_in_container(self):
+        """A library embedded in a container file is located by offset/size."""
+        lib = self.make_library()
+        container, offset, size = self.make_container(lib)
+        target = self.connect_accelerator(
+            [
+                {
+                    "pathname": container,
+                    "load": True,
+                    "load_address": 0x20000000,
+                    "file_offset": offset,
+                    "file_size": size,
+                }
+            ]
+        )
+
+        module = self.find_module(target, container)
+        self.assertIsNotNone(module, "embedded library should be loaded")
+        # The embedded object has .text at 0x3000; the outer one has it at
+        # 0x1000, so this only holds if the slice was used.
+        self.assert_text_loaded_at(target, module, 0x20003000)
+
+    def test_not_selected_for_host_target(self):
+        """The loader is not used for a non-accelerator architecture."""
+        self.server.responder = AcceleratorResponder([])
+        target = self.createTarget("host.yaml")
+        process = self.connect(target)
+        self.assertTrue(process.IsValid(), "Process is valid")
+
+        self.assertEqual(
+            self.server.responder.dyld_queries,
+            0,
+            "a host target must not query the accelerator dynamic loader",
+        )
diff --git a/lldb/test/API/accelerator/dynamic_loader/accelerator.yaml 
b/lldb/test/API/accelerator/dynamic_loader/accelerator.yaml
new file mode 100644
index 0000000000000..8a255dcba2de2
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/accelerator.yaml
@@ -0,0 +1,21 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_AMDGPU
+  OSABI:           ELFOSABI_AMDGPU_HSA
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1000
+    AddressAlign:    0x1000
+    Content:         "00000000"
+ProgramHeaders:
+  - Type:            PT_LOAD
+    Flags:           [ PF_X, PF_R ]
+    VAddr:           0x1000
+    Align:           0x1000
+    FirstSec:        .text
+    LastSec:         .text
diff --git a/lldb/test/API/accelerator/dynamic_loader/accelerator_lib.yaml 
b/lldb/test/API/accelerator/dynamic_loader/accelerator_lib.yaml
new file mode 100644
index 0000000000000..e7da8940a9fd3
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/accelerator_lib.yaml
@@ -0,0 +1,21 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_DYN
+  Machine:         EM_AMDGPU
+  OSABI:           ELFOSABI_AMDGPU_HSA
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1000
+    AddressAlign:    0x1000
+    Content:         "00000000"
+ProgramHeaders:
+  - Type:            PT_LOAD
+    Flags:           [ PF_X, PF_R ]
+    VAddr:           0x1000
+    Align:           0x1000
+    FirstSec:        .text
+    LastSec:         .text
diff --git a/lldb/test/API/accelerator/dynamic_loader/embedded_lib.yaml 
b/lldb/test/API/accelerator/dynamic_loader/embedded_lib.yaml
new file mode 100644
index 0000000000000..5d59cb1620de2
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/embedded_lib.yaml
@@ -0,0 +1,21 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_DYN
+  Machine:         EM_AMDGPU
+  OSABI:           ELFOSABI_AMDGPU_HSA
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x3000
+    AddressAlign:    0x1000
+    Content:         "00000000"
+ProgramHeaders:
+  - Type:            PT_LOAD
+    Flags:           [ PF_X, PF_R ]
+    VAddr:           0x3000
+    Align:           0x1000
+    FirstSec:        .text
+    LastSec:         .text
diff --git a/lldb/test/API/accelerator/dynamic_loader/host.yaml 
b/lldb/test/API/accelerator/dynamic_loader/host.yaml
new file mode 100644
index 0000000000000..a7efe4fed40b1
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/host.yaml
@@ -0,0 +1,20 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_X86_64
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x1000
+    AddressAlign:    0x1000
+    Content:         "c3"
+ProgramHeaders:
+  - Type:            PT_LOAD
+    Flags:           [ PF_X, PF_R ]
+    VAddr:           0x1000
+    Align:           0x1000
+    FirstSec:        .text
+    LastSec:         .text
diff --git 
a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp 
b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
index 8ee3510e67e0b..2174ad650a5b4 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
@@ -111,3 +111,15 @@ ProcessMockAccelerator::GetFileLoadAddress(const 
llvm::StringRef &file_name,
                                            lldb::addr_t &load_addr) {
   return Status::FromErrorString("unimplemented");
 }
+
+std::optional<AcceleratorDynamicLoaderResponse>
+ProcessMockAccelerator::GetAcceleratorDynamicLoaderLibraryInfos(
+    const AcceleratorDynamicLoaderArgs &args) {
+  AcceleratorDynamicLoaderResponse response;
+  AcceleratorDynamicLoaderLibraryInfo info;
+  info.pathname = "/path/to/lib.so";
+  info.load = true;
+  info.load_address = 0x10000000;
+  response.library_infos.push_back(std::move(info));
+  return response;
+}
diff --git 
a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h 
b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
index 6346773e40141..cbd473cdd19b1 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
@@ -60,6 +60,10 @@ class ProcessMockAccelerator : public NativeProcessProtocol {
   Status GetFileLoadAddress(const llvm::StringRef &file_name,
                             lldb::addr_t &load_addr) override;
 
+  std::optional<AcceleratorDynamicLoaderResponse>
+  GetAcceleratorDynamicLoaderLibraryInfos(
+      const AcceleratorDynamicLoaderArgs &args) override;
+
 private:
   mutable ArchSpec m_arch;
 };

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

Reply via email to