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

>From aa86f36dacdae734a51783308c0c129247f1b44c 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          | 34 ++++++++++
 .../Utility/AcceleratorGDBRemotePackets.h     | 64 +++++++++++++++++
 .../lldb/Utility/StringExtractorGDBRemote.h   |  1 +
 .../GDBRemoteCommunicationServerLLGS.cpp      | 33 +++++++++
 .../GDBRemoteCommunicationServerLLGS.h        |  3 +
 .../gdb-remote/LLDBServerAcceleratorPlugin.h  |  5 ++
 .../Utility/AcceleratorGDBRemotePackets.cpp   | 59 ++++++++++++++++
 .../Utility/StringExtractorGDBRemote.cpp      |  2 +
 .../mock/TestMockAcceleratorPackets.py        | 25 +++++++
 .../Mock/LLDBServerMockAcceleratorPlugin.cpp  | 12 ++++
 .../Mock/LLDBServerMockAcceleratorPlugin.h    |  3 +
 .../AcceleratorGDBRemotePacketsTest.cpp       | 68 +++++++++++++++++++
 12 files changed, 309 insertions(+)

diff --git a/lldb/docs/resources/lldbgdbremote.md 
b/lldb/docs/resources/lldbgdbremote.md
index d4a377b048c61..ec4b90a0e9cab 100644
--- a/lldb/docs/resources/lldbgdbremote.md
+++ b/lldb/docs/resources/lldbgdbremote.md
@@ -2865,3 +2865,37 @@ 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 all libraries. If false, return 
only updates since the last query. |
+
+The response JSON has the following fields:
+
+| Key             | Type  | Description |
+|-----------------|-------|-------------|
+| `library_infos` | array | Array of library info objects, each with 
`pathname`, `load` (bool), and optional `load_address`, `loaded_sections`, 
`uuid`, `native_memory_address`, `native_memory_size`, `file_offset`, 
`file_size`. |
+
+Example:
+```
+LLDB SENDS:    
jAcceleratorPluginGetDynamicLoaderLibraryInfo:{"plugin_name":"mock","full":true}
+STUB REPLIES:  
{"library_infos":[{"pathname":"/usr/lib/libmock_accel.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..b33b210c822ef 100644
--- a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
@@ -166,6 +166,70 @@ bool fromJSON(const llvm::json::Value &value,
               AcceleratorBreakpointHitResponse &data, llvm::json::Path path);
 llvm::json::Value toJSON(const AcceleratorBreakpointHitResponse &data);
 
+/// A section load address entry for dynamic loader library info.
+struct AcceleratorSectionInfo {
+  /// Hierarchical section names. If there are multiple names, each successive
+  /// name is looked up as a child section of the previous one. Example:
+  /// ["PT_LOAD[0]", ".text"].
+  std::vector<std::string> names;
+  /// The load address of this section.
+  uint64_t load_address = 0;
+};
+
+bool fromJSON(const llvm::json::Value &value, AcceleratorSectionInfo &data,
+              llvm::json::Path path);
+llvm::json::Value toJSON(const AcceleratorSectionInfo &data);
+
+/// Information about a shared library for the accelerator dynamic loader.
+struct AcceleratorDynamicLoaderLibraryInfo {
+  /// Path to the shared library object file on disk.
+  std::string pathname;
+  /// UUID of the shared library, if known.
+  std::optional<std::string> uuid_str;
+  /// True if loading, false if unloading.
+  bool load = true;
+  /// Base load address for the entire object file. If set, all sections are
+  /// slid to match. If not set, use \a loaded_sections or file addresses.
+  std::optional<uint64_t> load_address;
+  /// Per-section load addresses for object files with sections loaded at
+  /// different addresses.
+  std::vector<AcceleratorSectionInfo> loaded_sections;
+  /// Address in the native process where the object file image can be read.
+  std::optional<uint64_t> native_memory_address;
+  /// Size of the in-memory image starting at \a native_memory_address.
+  std::optional<uint64_t> native_memory_size;
+  /// Byte offset within the file specified by \a pathname.
+  std::optional<uint64_t> file_offset;
+  /// Size in bytes of the object file within the containing file.
+  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 {
+  /// Name of the accelerator plugin to query.
+  std::string plugin_name;
+  /// If true, return all libraries. If false, return only updates since the
+  /// last query.
+  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 769705588fbdf..899e18ee0e657 100644
--- 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -231,6 +231,11 @@ void 
GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
           eServerPacketType_jAcceleratorPluginBreakpointHit,
       &GDBRemoteCommunicationServerLLGS::
           Handle_jAcceleratorPluginBreakpointHit);
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::
+          eServerPacketType_jAcceleratorPluginGetDynamicLoaderLibraryInfo,
+      &GDBRemoteCommunicationServerLLGS::
+          Handle_jAcceleratorPluginGetDynamicLoaderLibraryInfo);
 
   RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g,
                                 &GDBRemoteCommunicationServerLLGS::Handle_g);
@@ -4637,3 +4642,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..6754e344427a7 100644
--- a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
+++ b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
@@ -142,4 +142,63 @@ json::Value toJSON(const AcceleratorBreakpointHitResponse 
&data) {
   return obj;
 }
 
+bool fromJSON(const 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 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.map("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 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 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..7cf662617e649 100644
--- a/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py
+++ b/lldb/test/API/accelerator/mock/TestMockAcceleratorPackets.py
@@ -186,3 +186,28 @@ 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()
+
+        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"], "/usr/lib/libmock_accel.so")
+        self.assertTrue(lib["load"])
+        self.assertEqual(lib["load_address"], 0x7F000000)
diff --git 
a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
 
b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/LLDBServerMockAcceleratorPlugin.cpp
index 6c1d23fef8a71..4433c6586a499 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 = "/usr/lib/libmock_accel.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..8f5d85718ac25 100644
--- a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
+++ b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
@@ -244,3 +244,71 @@ 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);
+}

>From cd906b677da2225067bdb8b34820dfad1f0f9076 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.

Selection is by name, not by triple: the target queries the server with a new
jLLDBSettings packet, and ProcessGDBRemote::GetDynamicLoader uses the returned
dyld_plugin_name ("accelerator-gdb-remote") to pick the loader. The loader 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 plugin implements the new NativeProcessProtocol hooks so
a self-contained end-to-end API test can exercise two library-provision paths:
a shared object on disk, and one embedded in a container file. Adds a
jLLDBSettings JSON round-trip unit test.
---
 .../lldb/Host/common/NativeProcessProtocol.h  |  15 ++
 .../Utility/AcceleratorGDBRemotePackets.h     |  12 ++
 .../lldb/Utility/StringExtractorGDBRemote.h   |   1 +
 .../AcceleratorGDBRemote/CMakeLists.txt       |  13 ++
 .../DynamicLoaderAcceleratorGDBRemote.cpp     | 155 ++++++++++++++++++
 .../DynamicLoaderAcceleratorGDBRemote.h       |  52 ++++++
 .../Plugins/DynamicLoader/CMakeLists.txt      |   1 +
 .../GDBRemoteCommunicationClient.cpp          |  50 ++++++
 .../gdb-remote/GDBRemoteCommunicationClient.h |  13 ++
 .../GDBRemoteCommunicationServerLLGS.cpp      |  57 +++++--
 .../GDBRemoteCommunicationServerLLGS.h        |   2 +
 .../Process/gdb-remote/ProcessGDBRemote.cpp   |  12 +-
 .../Utility/AcceleratorGDBRemotePackets.cpp   |   9 +
 .../Utility/StringExtractorGDBRemote.cpp      |   2 +
 .../API/accelerator/dynamic_loader/Makefile   |  11 ++
 .../TestMockAcceleratorDynamicLoader.py       | 140 ++++++++++++++++
 .../API/accelerator/dynamic_loader/gpu_lib.c  |   6 +
 .../API/accelerator/dynamic_loader/main.c     |  20 +++
 .../Mock/ProcessMockAccelerator.cpp           |  45 +++++
 .../Accelerator/Mock/ProcessMockAccelerator.h |   6 +
 .../AcceleratorGDBRemotePacketsTest.cpp       |   9 +
 21 files changed, 616 insertions(+), 15 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/Makefile
 create mode 100644 
lldb/test/API/accelerator/dynamic_loader/TestMockAcceleratorDynamicLoader.py
 create mode 100644 lldb/test/API/accelerator/dynamic_loader/gpu_lib.c
 create mode 100644 lldb/test/API/accelerator/dynamic_loader/main.c

diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h 
b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 435185a38f3f9..0c53d394070bb 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/Status.h"
@@ -162,6 +163,20 @@ class NativeProcessProtocol {
                                    "Not implemented");
   }
 
+  /// Return LLDB settings for this process (e.g. which DynamicLoader plugin to
+  /// use), or std::nullopt to use the defaults. Used to answer 
"jLLDBSettings".
+  virtual std::optional<LLDBSettings> GetLLDBSettings() { return std::nullopt; 
}
+
+  /// Return the accelerator dynamic loader library infos for this process, or
+  /// std::nullopt if the process does not support address-space aware dynamic
+  /// loading. Used to answer "jAcceleratorPluginGetDynamicLoaderLibraryInfo" 
on
+  /// a GPU connection.
+  virtual std::optional<AcceleratorDynamicLoaderResponse>
+  GetAcceleratorDynamicLoaderLibraryInfos(
+      const AcceleratorDynamicLoaderArgs &args) {
+    return std::nullopt;
+  }
+
   virtual bool HasPendingLibraryEvents() { return false; }
 
   virtual bool IsAlive() const;
diff --git a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h 
b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
index b33b210c822ef..d0c7b789e06d5 100644
--- a/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
+++ b/lldb/include/lldb/Utility/AcceleratorGDBRemotePackets.h
@@ -230,6 +230,18 @@ bool fromJSON(const llvm::json::Value &value,
               AcceleratorDynamicLoaderResponse &data, llvm::json::Path path);
 llvm::json::Value toJSON(const AcceleratorDynamicLoaderResponse &data);
 
+/// LLDB-specific settings a server reports for a target via the 
"jLLDBSettings"
+/// packet.
+struct LLDBSettings {
+  /// Name of the DynamicLoader plugin LLDB should use for this target. If
+  /// empty, the loader is auto-selected from the target triple.
+  std::string dyld_plugin_name;
+};
+
+bool fromJSON(const llvm::json::Value &value, LLDBSettings &data,
+              llvm::json::Path path);
+llvm::json::Value toJSON(const LLDBSettings &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 0236891dfcde1..cee9ef89f424e 100644
--- a/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
+++ b/lldb/include/lldb/Utility/StringExtractorGDBRemote.h
@@ -177,6 +177,7 @@ class StringExtractorGDBRemote : public StringExtractor {
     eServerPacketType_jAcceleratorPluginInitialize,
     eServerPacketType_jAcceleratorPluginBreakpointHit,
     eServerPacketType_jAcceleratorPluginGetDynamicLoaderLibraryInfo,
+    eServerPacketType_jLLDBSettings,
 
     eServerPacketType_qMemTags, // read memory tags
     eServerPacketType_QMemTags, // write memory tags
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..1ae10e0e67209
--- /dev/null
+++ 
b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.cpp
@@ -0,0 +1,155 @@
+//===-- 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) {
+  // Only create an instance when asked for by name; this loader is selected 
via
+  // the target's jLLDBSettings dyld_plugin_name, never auto-selected.
+  if (force)
+    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);
+
+  ProcessGDBRemote *gpu_process = static_cast<ProcessGDBRemote *>(m_process);
+  AcceleratorDynamicLoaderArgs args;
+  args.full = full;
+
+  Target &target = m_process->GetTarget();
+  ModuleList loaded_module_list;
+  std::optional<AcceleratorDynamicLoaderResponse> response =
+      
gpu_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);
+
+    // The object file is either a whole file (pathname), or a slice of a
+    // containing file located by file_offset/file_size (e.g. a library 
embedded
+    // in the executable via an llvm-objcopy section).
+    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) {
+      // Slide the whole object file to this base load address.
+      module_sp->SetLoadAddress(target, *info.load_address,
+                                /*value_is_offset=*/true, changed);
+    } else if (!info.loaded_sections.empty()) {
+      // Load individual sections at their own addresses. Each entry names a
+      // section, or a path of nested section names to descend.
+      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 {
+      // Load at the file addresses with no slide.
+      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 for accelerator (GPU) targets. Gets library load and "
+         "unload info from an lldb-server accelerator plugin over gdb-remote "
+         "rather than from a rendezvous breakpoint.";
+}
diff --git 
a/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.h
 
b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.h
new file mode 100644
index 0000000000000..068a52a8e778e
--- /dev/null
+++ 
b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.h
@@ -0,0 +1,52 @@
+//===-- 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 served by an lldb-server
+/// accelerator plugin.
+///
+/// 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 the
+/// "jAcceleratorPluginGetDynamicLoaderLibraryInfo" packet and loads the 
modules
+/// into the target. It is selected by name (not by triple), via the target's
+/// "jLLDBSettings" dyld_plugin_name, so it is only used for accelerator
+/// targets.
+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:
+  /// Query the GDB server for the accelerator's loaded libraries and load them
+  /// into the target. If \a full is false, only apply updates since the last
+  /// query. 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 a3fe6661737a4..ac7635055a979 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -298,6 +298,56 @@ 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;
+}
+
+std::optional<LLDBSettings> GDBRemoteCommunicationClient::GetLLDBSettings() {
+  if (m_supports_lldb_settings == eLazyBoolNo)
+    return std::nullopt;
+  if (m_lldb_settings)
+    return m_lldb_settings;
+
+  StringExtractorGDBRemote response;
+  response.SetResponseValidatorToJSON();
+  if (SendPacketAndWaitForResponse("jLLDBSettings", response) !=
+      PacketResult::Success)
+    return std::nullopt;
+  if (response.IsUnsupportedResponse() || response.IsErrorResponse()) {
+    m_supports_lldb_settings = eLazyBoolNo;
+    return std::nullopt;
+  }
+
+  llvm::Expected<LLDBSettings> parsed =
+      llvm::json::parse<LLDBSettings>(response.Peek(), "LLDBSettings");
+  if (!parsed) {
+    llvm::consumeError(parsed.takeError());
+    return std::nullopt;
+  }
+  m_supports_lldb_settings = eLazyBoolYes;
+  m_lldb_settings = *parsed;
+  return m_lldb_settings;
+}
+
 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..a52ac3416716b 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -377,6 +377,17 @@ class GDBRemoteCommunicationClient : public 
GDBRemoteClientBase {
   llvm::Expected<AcceleratorBreakpointHitResponse>
   AcceleratorBreakpointHit(const AcceleratorBreakpointHitArgs &args);
 
+  /// Send the "jAcceleratorPluginGetDynamicLoaderLibraryInfo" packet and 
return
+  /// the libraries the plugin (or the process) reports, or std::nullopt on
+  /// error.
+  std::optional<AcceleratorDynamicLoaderResponse>
+  GetAcceleratorDynamicLoaderLibraryInfos(
+      const AcceleratorDynamicLoaderArgs &args);
+
+  /// Send the "jLLDBSettings" packet and return the target's LLDB settings
+  /// (cached after the first query), or std::nullopt if unsupported.
+  std::optional<LLDBSettings> GetLLDBSettings();
+
   LazyBool SupportsAllocDeallocMemory() // const
   {
     // Uncomment this to have lldb pretend the debug server doesn't respond to
@@ -614,6 +625,8 @@ class GDBRemoteCommunicationClient : public 
GDBRemoteClientBase {
   LazyBool m_supports_multi_mem_read = eLazyBoolCalculate;
   LazyBool m_supports_multi_breakpoint = eLazyBoolCalculate;
   LazyBool m_supports_accelerator_plugins = eLazyBoolCalculate;
+  LazyBool m_supports_lldb_settings = eLazyBoolCalculate;
+  std::optional<LLDBSettings> m_lldb_settings;
 
   bool m_supports_qProcessInfoPID : 1, m_supports_qfProcessInfo : 1,
       m_supports_qUserName : 1, m_supports_qGroupName : 1,
diff --git 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index 899e18ee0e657..4fbf9ece31788 100644
--- 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -236,6 +236,9 @@ void 
GDBRemoteCommunicationServerLLGS::RegisterPacketHandlers() {
           eServerPacketType_jAcceleratorPluginGetDynamicLoaderLibraryInfo,
       &GDBRemoteCommunicationServerLLGS::
           Handle_jAcceleratorPluginGetDynamicLoaderLibraryInfo);
+  RegisterMemberFunctionHandler(
+      StringExtractorGDBRemote::eServerPacketType_jLLDBSettings,
+      &GDBRemoteCommunicationServerLLGS::Handle_jLLDBSettings);
 
   RegisterMemberFunctionHandler(StringExtractorGDBRemote::eServerPacketType_g,
                                 &GDBRemoteCommunicationServerLLGS::Handle_g);
@@ -4653,20 +4656,48 @@ 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 CPU 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 GPU 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());
+}
+
+GDBRemoteCommunication::PacketResult
+GDBRemoteCommunicationServerLLGS::Handle_jLLDBSettings(
+    StringExtractorGDBRemote &packet) {
+  if (!m_current_process)
+    return SendErrorResponse(Status::FromErrorString("no current process"));
+  std::optional<LLDBSettings> settings = m_current_process->GetLLDBSettings();
+  if (!settings)
+    return SendUnimplementedResponse("jLLDBSettings");
+  StreamGDBRemote stream;
+  stream.PutAsJSON(*settings, /*hex_ascii=*/false);
+  return SendPacketNoLock(stream.GetString());
 }
diff --git 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
index 3c8afe699bbaf..e9d074ff68208 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.h
@@ -305,6 +305,8 @@ class GDBRemoteCommunicationServerLLGS
   PacketResult Handle_jAcceleratorPluginGetDynamicLoaderLibraryInfo(
       StringExtractorGDBRemote &packet);
 
+  PacketResult Handle_jLLDBSettings(StringExtractorGDBRemote &packet);
+
   void SetCurrentThreadID(lldb::tid_t tid);
 
   lldb::tid_t GetCurrentThreadID() const;
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp 
b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index da45d79ba1118..aebbac2836010 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -4525,8 +4525,16 @@ bool ProcessGDBRemote::StopNoticingNewThreads() {
 }
 
 DynamicLoader *ProcessGDBRemote::GetDynamicLoader() {
-  if (m_dyld_up.get() == nullptr)
-    m_dyld_up.reset(DynamicLoader::FindPlugin(this, ""));
+  if (m_dyld_up.get() == nullptr) {
+    // The GDB server can name a specific DynamicLoader plugin via 
jLLDBSettings
+    // (e.g. accelerator targets select "accelerator-gdb-remote"). Otherwise 
the
+    // loader is auto-selected from the target triple.
+    llvm::StringRef dyld_name;
+    std::optional<LLDBSettings> settings = m_gdb_comm.GetLLDBSettings();
+    if (settings)
+      dyld_name = settings->dyld_plugin_name;
+    m_dyld_up.reset(DynamicLoader::FindPlugin(this, dyld_name));
+  }
   return m_dyld_up.get();
 }
 
diff --git a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp 
b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
index 6754e344427a7..56ae3fe1a3852 100644
--- a/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
+++ b/lldb/source/Utility/AcceleratorGDBRemotePackets.cpp
@@ -201,4 +201,13 @@ json::Value toJSON(const AcceleratorDynamicLoaderResponse 
&data) {
   return Object{{"library_infos", data.library_infos}};
 }
 
+bool fromJSON(const Value &value, LLDBSettings &data, Path path) {
+  ObjectMapper o(value, path);
+  return o && o.map("dyld_plugin_name", data.dyld_plugin_name);
+}
+
+json::Value toJSON(const LLDBSettings &data) {
+  return Object{{"dyld_plugin_name", data.dyld_plugin_name}};
+}
+
 } // namespace lldb_private
diff --git a/lldb/source/Utility/StringExtractorGDBRemote.cpp 
b/lldb/source/Utility/StringExtractorGDBRemote.cpp
index 953a2cd3090d1..a8253d29ef285 100644
--- a/lldb/source/Utility/StringExtractorGDBRemote.cpp
+++ b/lldb/source/Utility/StringExtractorGDBRemote.cpp
@@ -340,6 +340,8 @@ StringExtractorGDBRemote::GetServerPacketType() const {
       return eServerPacketType_jAcceleratorPluginBreakpointHit;
     if (PACKET_STARTS_WITH("jAcceleratorPluginGetDynamicLoaderLibraryInfo:"))
       return eServerPacketType_jAcceleratorPluginGetDynamicLoaderLibraryInfo;
+    if (PACKET_MATCHES("jLLDBSettings"))
+      return eServerPacketType_jLLDBSettings;
     break;
 
   case 'v':
diff --git a/lldb/test/API/accelerator/dynamic_loader/Makefile 
b/lldb/test/API/accelerator/dynamic_loader/Makefile
new file mode 100644
index 0000000000000..5e95a3b38ed44
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/Makefile
@@ -0,0 +1,11 @@
+C_SOURCES := main.c
+
+include Makefile.rules
+
+# Build a real shared library the accelerator dynamic loader can load. a.out is
+# deliberately not linked against it: the test only needs the .so to exist on
+# disk so the loader can pull it into the accelerator target.
+all: libgpu_lib.so
+
+libgpu_lib.so: gpu_lib.c
+       $(CC) $(CFLAGS) -fPIC -shared -o "$@" $<
diff --git 
a/lldb/test/API/accelerator/dynamic_loader/TestMockAcceleratorDynamicLoader.py 
b/lldb/test/API/accelerator/dynamic_loader/TestMockAcceleratorDynamicLoader.py
new file mode 100644
index 0000000000000..f9c426eb5af2a
--- /dev/null
+++ 
b/lldb/test/API/accelerator/dynamic_loader/TestMockAcceleratorDynamicLoader.py
@@ -0,0 +1,140 @@
+"""
+End-to-end test for the accelerator dynamic loader.
+
+When the client creates and connects the accelerator target (triggered by the
+mock accelerator plugin's connection hook), that target selects the
+"accelerator-gdb-remote" dynamic loader via the jLLDBSettings packet, instead 
of
+auto-selecting a loader from the triple. The loader then asks the accelerator
+GDB server for its loaded libraries via
+jAcceleratorPluginGetDynamicLoaderLibraryInfo and loads them into the target.
+
+This verifies two of the ways a library can be provided:
+  1. As a whole shared object on disk.
+  3. As a shared object embedded in a containing file, located by file offset
+     and size (as a library added to a container with llvm-objcopy would be).
+
+The in-memory case (2) needs host support that is out of scope here, so it is
+not exercised.
+"""
+
+import os
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.lldbtest import *
+from lldbsuite.test import configuration
+
+
+class MockAcceleratorDynamicLoaderTestCase(TestBase):
+    NO_DEBUG_INFO_TESTCASE = True
+
+    def setUp(self):
+        super().setUp()
+        if "mock-accelerator" not in configuration.enabled_plugins:
+            self.skipTest("mock-accelerator plugin is not enabled")
+
+    def set_mock_env(self, name, value):
+        """Set an environment variable the mock plugin reads (it is inherited 
by
+        the lldb-server that hosts the plugin), restoring it after the test."""
+        previous = os.environ.get(name)
+        os.environ[name] = value
+
+        def restore():
+            if previous is None:
+                os.environ.pop(name, None)
+            else:
+                os.environ[name] = previous
+
+        self.addTearDownHook(restore)
+
+    def make_container(self, lib_path):
+        """Embed the library bytes in a larger container file, returning the
+        container path plus the (offset, size) of the embedded object."""
+        with open(lib_path, "rb") as f:
+            lib_bytes = f.read()
+        # Model a library embedded in a containing object file (as llvm-objcopy
+        # would add it to an executable): the container is itself a valid 
object
+        # file, with the embedded library located deeper in the file by
+        # file_offset/file_size. We reuse the library bytes as the outer object
+        # and page-align the embedded copy.
+        prefix = lib_bytes
+        pad = (-len(prefix)) % 0x1000
+        prefix += b"\x00" * pad
+        container_path = self.getBuildArtifact("container.bin")
+        with open(container_path, "wb") as f:
+            f.write(prefix)
+            f.write(lib_bytes)
+        return container_path, len(prefix), len(lib_bytes)
+
+    def find_module(self, target, basename):
+        for i in range(target.GetNumModules()):
+            module = target.GetModuleAtIndex(i)
+            if module.GetFileSpec().GetFilename() == basename:
+                return module
+        return None
+
+    def assert_loaded_at(self, target, module, base):
+        """The module's .text must resolve to a load address at or above the
+        base the loader slid it to, proving the load address was applied."""
+        section = module.FindSection(".text")
+        self.assertTrue(section.IsValid(), "library should have a .text 
section")
+        load_addr = section.GetLoadAddress(target)
+        self.assertNotEqual(
+            load_addr,
+            lldb.LLDB_INVALID_ADDRESS,
+            "library section should have a load address in the accelerator 
target",
+        )
+        self.assertGreaterEqual(load_addr, base)
+
+    @skipIfRemote
+    @add_test_categories(["llgs"])
+    def test_accelerator_dynamic_loader(self):
+        """The accelerator target loads the libraries reported by the 
server."""
+        self.build()
+        exe = self.getBuildArtifact("a.out")
+        lib_ondisk = self.getBuildArtifact("libgpu_lib.so")
+        container, offset, size = self.make_container(lib_ondisk)
+
+        # Tell the mock accelerator process which libraries to report, and 
where.
+        self.set_mock_env("LLDB_MOCK_ACCELERATOR_LIB_ONDISK", lib_ondisk)
+        self.set_mock_env("LLDB_MOCK_ACCELERATOR_LIB_CONTAINER", container)
+        self.set_mock_env("LLDB_MOCK_ACCELERATOR_LIB_OFFSET", str(offset))
+        self.set_mock_env("LLDB_MOCK_ACCELERATOR_LIB_SIZE", str(size))
+
+        target = self.dbg.CreateTarget(exe)
+        self.assertTrue(target, VALID_TARGET)
+
+        # Launch stops at the plugin's initialize breakpoint; continuing 
reaches
+        # the connection hook that creates the accelerator target.
+        process = target.LaunchSimple(None, None, 
self.get_process_working_directory())
+        self.assertTrue(process, PROCESS_IS_VALID)
+        self.assertState(process.GetState(), lldb.eStateStopped)
+        process.Continue()
+        self.assertState(process.GetState(), lldb.eStateStopped)
+
+        # The accelerator target now exists alongside the native target.
+        self.assertEqual(self.dbg.GetNumTargets(), 2)
+        accelerator_target = None
+        for i in range(self.dbg.GetNumTargets()):
+            candidate = self.dbg.GetTargetAtIndex(i)
+            if candidate != target:
+                accelerator_target = candidate
+                break
+        self.assertTrue(accelerator_target.IsValid())
+
+        # Scenario 1: the whole-file shared library was loaded.
+        ondisk_module = self.find_module(accelerator_target, "libgpu_lib.so")
+        self.assertIsNotNone(
+            ondisk_module,
+            "on-disk library should be loaded into the accelerator target",
+        )
+        self.assert_loaded_at(accelerator_target, ondisk_module, 0x10000000)
+
+        # Scenario 3: the embedded shared library, located by file offset/size,
+        # was loaded as a distinct module from its container file.
+        embedded_module = self.find_module(accelerator_target, "container.bin")
+        self.assertIsNotNone(
+            embedded_module,
+            "embedded library should be loaded into the accelerator target",
+        )
+        self.assert_loaded_at(accelerator_target, embedded_module, 0x20000000)
diff --git a/lldb/test/API/accelerator/dynamic_loader/gpu_lib.c 
b/lldb/test/API/accelerator/dynamic_loader/gpu_lib.c
new file mode 100644
index 0000000000000..00de86bc4351b
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/gpu_lib.c
@@ -0,0 +1,6 @@
+// A trivial shared library the accelerator dynamic loader loads into the
+// accelerator target. It only needs to be a real, parseable object file with
+// some code; the functions are never called.
+int gpu_lib_add(int a, int b) { return a + b; }
+
+int gpu_lib_mul(int a, int b) { return a * b; }
diff --git a/lldb/test/API/accelerator/dynamic_loader/main.c 
b/lldb/test/API/accelerator/dynamic_loader/main.c
new file mode 100644
index 0000000000000..18aed944b8d8f
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/main.c
@@ -0,0 +1,20 @@
+// The mock accelerator plugin sets its breakpoints on these dedicated, 
uniquely
+// named functions.
+void mock_gpu_accelerator_initialize(void) {}
+
+int mock_gpu_accelerator_compute(int x) { return x * 2; }
+
+int mock_gpu_accelerator_finish(void) { return 0; }
+
+// When the plugin's connection-trigger breakpoint on this function is hit, it
+// asks the client to create a second target and connect to the mock 
accelerator
+// GDB server.
+void mock_gpu_accelerator_connect(void) {}
+
+int main(void) {
+  mock_gpu_accelerator_initialize();
+  mock_gpu_accelerator_connect();
+  int result = mock_gpu_accelerator_compute(21);
+  mock_gpu_accelerator_finish();
+  return result == 42 ? 0 : 1;
+}
diff --git 
a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp 
b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
index 174ab1a0143d4..2abc957419e1f 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
@@ -13,6 +13,8 @@
 #include "lldb/Host/ProcessLaunchInfo.h"
 #include "llvm/Support/Error.h"
 
+#include <cstdlib>
+
 using namespace lldb;
 using namespace lldb_private;
 using namespace lldb_private::lldb_server;
@@ -110,3 +112,46 @@ ProcessMockAccelerator::GetFileLoadAddress(const 
llvm::StringRef &file_name,
                                            lldb::addr_t &load_addr) {
   return Status::FromErrorString("unimplemented");
 }
+
+std::optional<LLDBSettings> ProcessMockAccelerator::GetLLDBSettings() {
+  // Tell the client to use the accelerator dynamic loader for this target, so
+  // libraries are provided by this plugin rather than by a rendezvous
+  // breakpoint.
+  LLDBSettings settings;
+  settings.dyld_plugin_name = "accelerator-gdb-remote";
+  return settings;
+}
+
+std::optional<AcceleratorDynamicLoaderResponse>
+ProcessMockAccelerator::GetAcceleratorDynamicLoaderLibraryInfos(
+    const AcceleratorDynamicLoaderArgs &args) {
+  // The test drives which libraries to report (and where they live) via the
+  // environment, so it can build real object files and point us at them.
+  AcceleratorDynamicLoaderResponse response;
+
+  // Scenario 1: a shared library provided as a whole file on disk, loaded at a
+  // fixed base address.
+  if (const char *path = ::getenv("LLDB_MOCK_ACCELERATOR_LIB_ONDISK")) {
+    AcceleratorDynamicLoaderLibraryInfo info;
+    info.pathname = path;
+    info.load = true;
+    info.load_address = 0x10000000;
+    response.library_infos.push_back(std::move(info));
+  }
+
+  // Scenario 3: a shared library embedded in a container file (e.g. added to
+  // the executable with llvm-objcopy), located by file offset and size.
+  if (const char *path = ::getenv("LLDB_MOCK_ACCELERATOR_LIB_CONTAINER")) {
+    AcceleratorDynamicLoaderLibraryInfo info;
+    info.pathname = path;
+    info.load = true;
+    info.load_address = 0x20000000;
+    if (const char *offset = ::getenv("LLDB_MOCK_ACCELERATOR_LIB_OFFSET"))
+      info.file_offset = std::strtoull(offset, nullptr, 0);
+    if (const char *size = ::getenv("LLDB_MOCK_ACCELERATOR_LIB_SIZE"))
+      info.file_size = std::strtoull(size, nullptr, 0);
+    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 6fc07b5ac011a..482883da490bd 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.h
@@ -60,6 +60,12 @@ class ProcessMockAccelerator : public NativeProcessProtocol {
   Status GetFileLoadAddress(const llvm::StringRef &file_name,
                             lldb::addr_t &load_addr) override;
 
+  std::optional<LLDBSettings> GetLLDBSettings() override;
+
+  std::optional<AcceleratorDynamicLoaderResponse>
+  GetAcceleratorDynamicLoaderLibraryInfos(
+      const AcceleratorDynamicLoaderArgs &args) override;
+
 private:
   mutable ArchSpec m_arch;
 };
diff --git a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp 
b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
index 8f5d85718ac25..8f606100354c0 100644
--- a/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
+++ b/lldb/unittests/Utility/AcceleratorGDBRemotePacketsTest.cpp
@@ -312,3 +312,12 @@ TEST(AcceleratorGDBRemotePacketsTest, 
AcceleratorDynamicLoaderResponse) {
   EXPECT_EQ("/path/to/lib.so", deserialized->library_infos[0].pathname);
   EXPECT_TRUE(deserialized->library_infos[0].load);
 }
+
+TEST(AcceleratorGDBRemotePacketsTest, LLDBSettings) {
+  LLDBSettings settings;
+  settings.dyld_plugin_name = "accelerator-gdb-remote";
+
+  Expected<LLDBSettings> deserialized = roundtripJSON(settings);
+  ASSERT_THAT_EXPECTED(deserialized, Succeeded());
+  EXPECT_EQ(settings.dyld_plugin_name, deserialized->dyld_plugin_name);
+}

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

Reply via email to