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

>From 0b374e3d497605fcd868d29c06bd6121335cc93f Mon Sep 17 00:00:00 2001
From: satya janga <[email protected]>
Date: Tue, 8 Sep 2026 14:46:31 -0700
Subject: [PATCH 1/2] [lldb] Add a Clang offload bundle object container

Discover Clang offload bundles embedded in ELF files and expose their device 
images as module specifications. Preserve the outer object specification and 
load compressed device images from owned in-memory extractors.

Based on the original llvm-server-plugins implementation:

https://github.com/clayborg/llvm-project/commit/4b255f4318cac2b7e892d427bd7b765f4730dc9e
---
 .../Plugins/ObjectContainer/CMakeLists.txt    |   1 +
 .../Clang-Offload-Bundle/CMakeLists.txt       |  13 +
 .../ObjectContainerClangOffloadBundle.cpp     | 243 ++++++++++++
 .../ObjectContainerClangOffloadBundle.h       |  80 ++++
 lldb/source/Symbol/ObjectFile.cpp             |  21 +-
 lldb/unittests/ObjectContainer/CMakeLists.txt |   3 +
 .../ObjectContainerClangOffloadBundleTest.cpp | 360 ++++++++++++++++++
 7 files changed, 714 insertions(+), 7 deletions(-)
 create mode 100644 
lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/CMakeLists.txt
 create mode 100644 
lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
 create mode 100644 
lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.h
 create mode 100644 
lldb/unittests/ObjectContainer/ObjectContainerClangOffloadBundleTest.cpp

diff --git a/lldb/source/Plugins/ObjectContainer/CMakeLists.txt 
b/lldb/source/Plugins/ObjectContainer/CMakeLists.txt
index 220a76d1159d5..8cf7157cd0ad5 100644
--- a/lldb/source/Plugins/ObjectContainer/CMakeLists.txt
+++ b/lldb/source/Plugins/ObjectContainer/CMakeLists.txt
@@ -2,5 +2,6 @@ set_property(DIRECTORY PROPERTY LLDB_PLUGIN_KIND 
ObjectContainer)
 
 add_subdirectory(BSD-Archive)
 add_subdirectory(Big-Archive) 
+add_subdirectory(Clang-Offload-Bundle)
 add_subdirectory(Universal-Mach-O)
 add_subdirectory(Mach-O-Fileset)
diff --git 
a/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/CMakeLists.txt 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/CMakeLists.txt
new file mode 100644
index 0000000000000..9a3582759b4a5
--- /dev/null
+++ b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/CMakeLists.txt
@@ -0,0 +1,13 @@
+add_lldb_library(lldbPluginObjectContainerClangOffloadBundle PLUGIN
+  ObjectContainerClangOffloadBundle.cpp
+
+  LINK_COMPONENTS
+    BinaryFormat
+    Object
+  LINK_LIBS
+    lldbCore
+    lldbHost
+    lldbSymbol
+    lldbTarget
+    lldbUtility
+  )
diff --git 
a/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
new file mode 100644
index 0000000000000..9d785ee1fc719
--- /dev/null
+++ 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
@@ -0,0 +1,243 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "ObjectContainerClangOffloadBundle.h"
+
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataBufferHeap.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/OffloadBundle.h"
+#include "llvm/Support/MemoryBuffer.h"
+
+#include <string>
+#include <utility>
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(ObjectContainerClangOffloadBundle)
+
+void ObjectContainerClangOffloadBundle::Initialize() {
+  PluginManager::RegisterPlugin(
+      GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
+      GetModuleSpecifications, /*create_memory_callback=*/nullptr);
+}
+
+void ObjectContainerClangOffloadBundle::Terminate() {
+  PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+ObjectContainerClangOffloadBundle::ObjectContainerClangOffloadBundle(
+    const ModuleSP &module_sp, DataBufferSP &data_sp,
+    lldb::offset_t data_offset, const FileSpec *file,
+    lldb::offset_t file_offset, lldb::offset_t length)
+    : ObjectContainer(module_sp, file, file_offset, length, data_sp,
+                      data_offset) {}
+
+ObjectContainerClangOffloadBundle::~ObjectContainerClangOffloadBundle() =
+    default;
+
+bool ObjectContainerClangOffloadBundle::MagicBytesMatch(
+    const DataExtractor &data) {
+  llvm::StringRef bytes(reinterpret_cast<const char *>(data.GetDataStart()),
+                        data.GetByteSize());
+  switch (llvm::identify_magic(bytes)) {
+  case llvm::file_magic::elf:
+  case llvm::file_magic::elf_relocatable:
+  case llvm::file_magic::elf_executable:
+  case llvm::file_magic::elf_shared_object:
+  case llvm::file_magic::elf_core:
+    return true;
+  default:
+    return false;
+  }
+}
+
+static ArchSpec ParseArchFromBundleEntryID(llvm::StringRef id) {
+  // Bundle entry IDs use the format: <offload-kind>-<target-triple>
+  // e.g. "hip-amdgpu-amd-amdhsa--gfx906", "host-x86_64-unknown-linux-gnu"
+  llvm::StringRef triple = id.split('-').second;
+  if (triple.empty())
+    return {};
+  return ArchSpec(triple);
+}
+
+bool ObjectContainerClangOffloadBundle::FindBundleEntries(
+    const FileSpec &file, DataExtractorSP extractor_sp,
+    lldb::offset_t file_offset, lldb::offset_t file_size,
+    std::vector<Entry> &entries) {
+  const uint8_t *data = nullptr;
+  size_t data_size = 0;
+  DataBufferSP mapped_data_sp;
+  if (extractor_sp && extractor_sp->HasData() &&
+      extractor_sp->GetByteSize() >= file_size) {
+    data = extractor_sp->GetDataStart();
+    data_size = file_size ? file_size : extractor_sp->GetByteSize();
+  } else if (file) {
+    mapped_data_sp =
+        FileSystem::Instance().CreateDataBuffer(file, file_size, file_offset);
+    if (mapped_data_sp) {
+      data = mapped_data_sp->GetBytes();
+      data_size = mapped_data_sp->GetByteSize();
+    }
+  }
+  if (!data)
+    return false;
+
+  llvm::StringRef bytes(reinterpret_cast<const char *>(data), data_size);
+  const std::string path = file.GetPath();
+  llvm::MemoryBufferRef buffer(bytes, path);
+  auto object_or_error = llvm::object::ObjectFile::createObjectFile(buffer);
+  if (!object_or_error) {
+    LLDB_LOG_ERROR(GetLog(LLDBLog::Object), object_or_error.takeError(),
+                   "unable to parse clang offload bundle container: {0}");
+    return false;
+  }
+
+  llvm::SmallVector<llvm::object::OffloadBundleFatBin> bundles;
+  if (llvm::Error error = llvm::object::extractOffloadBundleFatBinary(
+          **object_or_error, bundles)) {
+    LLDB_LOG_ERROR(GetLog(LLDBLog::Object), std::move(error),
+                   "unable to extract clang offload bundle: {0}");
+    return false;
+  }
+
+  for (llvm::object::OffloadBundleFatBin &bundle : bundles) {
+    for (const llvm::object::OffloadBundleEntry &bundle_entry :
+         bundle.getEntries()) {
+      if (bundle_entry.Size == 0)
+        continue;
+
+      DataExtractorSP entry_extractor_sp;
+      uint64_t entry_offset = 0;
+      if (bundle.isDecompressed()) {
+        if (!bundle.DecompressedBuffer)
+          continue;
+
+        llvm::StringRef decompressed = bundle.DecompressedBuffer->getBuffer();
+        if (bundle_entry.Offset > decompressed.size() ||
+            bundle_entry.Size > decompressed.size() - bundle_entry.Offset)
+          continue;
+
+        auto entry_data_sp = std::make_shared<DataBufferHeap>(
+            decompressed.data() + bundle_entry.Offset, bundle_entry.Size);
+        entry_extractor_sp = std::make_shared<DataExtractor>(entry_data_sp);
+      } else {
+        entry_offset = file_offset + bundle_entry.Offset;
+      }
+
+      ArchSpec arch = ParseArchFromBundleEntryID(bundle_entry.ID);
+      if (!arch.IsValid())
+        continue;
+
+      entries.push_back({std::move(arch), entry_offset, bundle_entry.Size,
+                         std::move(entry_extractor_sp)});
+    }
+  }
+
+  return !entries.empty();
+}
+
+ObjectContainer *ObjectContainerClangOffloadBundle::CreateInstance(
+    const ModuleSP &module_sp, DataBufferSP &data_sp,
+    lldb::offset_t data_offset, const FileSpec *file,
+    lldb::offset_t file_offset, lldb::offset_t length) {
+  if (!data_sp || !file)
+    return nullptr;
+
+  DataExtractor data;
+  data.SetData(data_sp, data_offset, length);
+  if (!MagicBytesMatch(data))
+    return nullptr;
+
+  auto container_up = std::make_unique<ObjectContainerClangOffloadBundle>(
+      module_sp, data_sp, data_offset, file, file_offset, length);
+  if (!container_up->ParseHeader())
+    return nullptr;
+  return container_up.release();
+}
+
+bool ObjectContainerClangOffloadBundle::ParseHeader() {
+  m_entries.clear();
+  return FindBundleEntries(m_file, m_extractor_sp, m_offset, m_length,
+                           m_entries);
+}
+
+size_t ObjectContainerClangOffloadBundle::GetNumArchitectures() const {
+  return m_entries.size();
+}
+
+bool ObjectContainerClangOffloadBundle::GetArchitectureAtIndex(
+    uint32_t idx, ArchSpec &arch) const {
+  if (idx >= m_entries.size())
+    return false;
+  arch = m_entries[idx].arch;
+  return true;
+}
+
+ModuleSpecList ObjectContainerClangOffloadBundle::GetModuleSpecifications(
+    const FileSpec &file, DataExtractorSP &extractor_sp,
+    lldb::offset_t file_offset, lldb::offset_t file_size) {
+  if (!extractor_sp || !MagicBytesMatch(*extractor_sp))
+    return {};
+
+  std::vector<Entry> entries;
+  if (!FindBundleEntries(file, extractor_sp, file_offset, file_size, entries))
+    return {};
+
+  ModuleSpecList specs;
+  for (const Entry &entry : entries) {
+    ModuleSpec spec = entry.extractor_sp
+                          ? ModuleSpec(file, UUID(), entry.extractor_sp)
+                          : ModuleSpec(file, entry.arch);
+    if (entry.extractor_sp)
+      spec.GetArchitecture() = entry.arch;
+    spec.SetObjectOffset(entry.offset);
+    spec.SetObjectSize(entry.size);
+    specs.Append(spec);
+  }
+  return specs;
+}
+
+ObjectFileSP
+ObjectContainerClangOffloadBundle::GetObjectFile(const FileSpec *file) {
+  ModuleSP module_sp = GetModule();
+  if (!module_sp)
+    return {};
+
+  ArchSpec arch = module_sp->GetArchitecture();
+  if (!arch.IsValid()) {
+    arch = Target::GetDefaultArchitecture();
+    if (!arch.IsValid())
+      arch.SetTriple(LLDB_ARCH_DEFAULT);
+  }
+
+  for (int pass = 0; pass < 2; ++pass) {
+    for (const Entry &entry : m_entries) {
+      bool match = (pass == 0) ? arch.IsExactMatch(entry.arch)
+                               : arch.IsCompatibleMatch(entry.arch);
+      if (match) {
+        DataExtractorSP extractor_sp = entry.extractor_sp;
+        lldb::offset_t data_offset = 0;
+        return ObjectFile::FindPlugin(module_sp, file, entry.offset, 
entry.size,
+                                      extractor_sp, data_offset);
+      }
+    }
+  }
+
+  return {};
+}
diff --git 
a/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.h
 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.h
new file mode 100644
index 0000000000000..da445023c1aa4
--- /dev/null
+++ 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.h
@@ -0,0 +1,80 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_OBJECTCONTAINER_CLANG_OFFLOAD_BUNDLE_OBJECTCONTAINERCLANGOFFLOADBUNDLE_H
+#define 
LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_CLANG_OFFLOAD_BUNDLE_OBJECTCONTAINERCLANGOFFLOADBUNDLE_H
+
+#include "lldb/Symbol/ObjectContainer.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/FileSpec.h"
+
+#include <vector>
+
+class ObjectContainerClangOffloadBundle : public lldb_private::ObjectContainer 
{
+public:
+  ObjectContainerClangOffloadBundle(const lldb::ModuleSP &module_sp,
+                                    lldb::DataBufferSP &data_sp,
+                                    lldb::offset_t data_offset,
+                                    const lldb_private::FileSpec *file,
+                                    lldb::offset_t file_offset,
+                                    lldb::offset_t length);
+
+  ~ObjectContainerClangOffloadBundle() override;
+
+  static void Initialize();
+  static void Terminate();
+
+  static llvm::StringRef GetPluginNameStatic() {
+    return "clang-offload-bundle";
+  }
+
+  static llvm::StringRef GetPluginDescriptionStatic() {
+    return "Clang offload bundle object container reader for ELF files.";
+  }
+
+  static lldb_private::ObjectContainer *
+  CreateInstance(const lldb::ModuleSP &module_sp, lldb::DataBufferSP &data_sp,
+                 lldb::offset_t data_offset, const lldb_private::FileSpec 
*file,
+                 lldb::offset_t file_offset, lldb::offset_t length);
+
+  static lldb_private::ModuleSpecList
+  GetModuleSpecifications(const lldb_private::FileSpec &file,
+                          lldb::DataExtractorSP &extractor_sp,
+                          lldb::offset_t file_offset, lldb::offset_t 
file_size);
+
+  static bool MagicBytesMatch(const lldb_private::DataExtractor &data);
+
+  bool ParseHeader() override;
+
+  size_t GetNumArchitectures() const override;
+
+  bool GetArchitectureAtIndex(uint32_t idx,
+                              lldb_private::ArchSpec &arch) const override;
+
+  lldb::ObjectFileSP GetObjectFile(const lldb_private::FileSpec *file) 
override;
+
+  llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+
+protected:
+  struct Entry {
+    lldb_private::ArchSpec arch;
+    uint64_t offset = 0;
+    uint64_t size = 0;
+    lldb::DataExtractorSP extractor_sp;
+  };
+
+  std::vector<Entry> m_entries;
+
+  static bool FindBundleEntries(const lldb_private::FileSpec &file,
+                                lldb::DataExtractorSP extractor_sp,
+                                lldb::offset_t file_offset,
+                                lldb::offset_t file_size,
+                                std::vector<Entry> &entries);
+};
+
+#endif // 
LLDB_SOURCE_PLUGINS_OBJECTCONTAINER_CLANG_OFFLOAD_BUNDLE_OBJECTCONTAINERCLANGOFFLOADBUNDLE_H
diff --git a/lldb/source/Symbol/ObjectFile.cpp 
b/lldb/source/Symbol/ObjectFile.cpp
index 89d01568de926..ce30d2e92d13e 100644
--- a/lldb/source/Symbol/ObjectFile.cpp
+++ b/lldb/source/Symbol/ObjectFile.cpp
@@ -220,22 +220,29 @@ ModuleSpecList ObjectFile::GetModuleSpecifications(
 ModuleSpecList ObjectFile::GetModuleSpecifications(
     const lldb_private::FileSpec &file, lldb::DataExtractorSP &extractor_sp,
     lldb::offset_t file_offset, lldb::offset_t file_size) {
+  ModuleSpecList specs;
+
+  // A container can be embedded in an otherwise valid object file. Preserve
+  // the object file's specifications and also query the container plug-ins.
+
   // Try the ObjectFile plug-ins
   for (auto &cbs : PluginManager::GetObjectFileCallbacks()) {
-    ModuleSpecList specs = cbs.get_module_specifications(
+    ModuleSpecList object_specs = cbs.get_module_specifications(
         file, extractor_sp, file_offset, file_size);
-    if (specs.GetSize() > 0)
-      return specs;
+    if (object_specs.GetSize() > 0) {
+      specs.Append(object_specs);
+      break;
+    }
   }
 
   // Try the ObjectContainer plug-ins
   for (auto &cbs : PluginManager::GetObjectContainerCallbacks()) {
-    ModuleSpecList specs = cbs.get_module_specifications(
+    ModuleSpecList container_specs = cbs.get_module_specifications(
         file, extractor_sp, file_offset, file_size);
-    if (specs.GetSize() > 0)
-      return specs;
+    if (container_specs.GetSize() > 0)
+      specs.Append(container_specs);
   }
-  return {};
+  return specs;
 }
 
 ObjectFile::ObjectFile(const lldb::ModuleSP &module_sp,
diff --git a/lldb/unittests/ObjectContainer/CMakeLists.txt 
b/lldb/unittests/ObjectContainer/CMakeLists.txt
index e4ca2df0202a9..9f28a1403d02d 100644
--- a/lldb/unittests/ObjectContainer/CMakeLists.txt
+++ b/lldb/unittests/ObjectContainer/CMakeLists.txt
@@ -1,9 +1,12 @@
 add_lldb_unittest(ObjectContainerTests
+  ObjectContainerClangOffloadBundleTest.cpp
   ObjectContainerUniversalMachOTest.cpp
 
   LINK_LIBS
+    lldbPluginObjectContainerClangOffloadBundle
     lldbPluginObjectContainerMachOArchive
     lldbPluginObjectContainerMachOFileset
+    lldbPluginObjectFileELF
     lldbPluginObjectFileMachO
     lldbCore
     lldbUtilityHelpers
diff --git 
a/lldb/unittests/ObjectContainer/ObjectContainerClangOffloadBundleTest.cpp 
b/lldb/unittests/ObjectContainer/ObjectContainerClangOffloadBundleTest.cpp
new file mode 100644
index 0000000000000..5f2e4510c1520
--- /dev/null
+++ b/lldb/unittests/ObjectContainer/ObjectContainerClangOffloadBundleTest.cpp
@@ -0,0 +1,360 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 
"Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.h"
+#include "Plugins/ObjectFile/ELF/ObjectFileELF.h"
+#include "TestingSupport/SubsystemRAII.h"
+#include "TestingSupport/TestUtilities.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Host/HostInfo.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Object/OffloadBundle.h"
+#include "llvm/Support/Compression.h"
+#include "llvm/Support/FileUtilities.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Testing/Support/Error.h"
+#include "gtest/gtest.h"
+
+#include <array>
+#include <cstdint>
+#include <string>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+constexpr uint64_t BundleSectionOffset = 0x1000;
+constexpr llvm::StringLiteral BundleMagic = "__CLANG_OFFLOAD_BUNDLE__";
+
+void AppendU64(std::vector<uint8_t> &bytes, uint64_t value) {
+  for (unsigned i = 0; i != 8; ++i)
+    bytes.push_back(static_cast<uint8_t>(value >> (i * 8)));
+}
+
+std::vector<uint8_t> MakeBundle(llvm::StringRef id,
+                                llvm::ArrayRef<uint8_t> payload) {
+  std::vector<uint8_t> bytes(BundleMagic.bytes_begin(),
+                             BundleMagic.bytes_end());
+  AppendU64(bytes, 1);
+  const uint64_t payload_offset =
+      BundleMagic.size() + 4 * sizeof(uint64_t) + id.size();
+  AppendU64(bytes, payload_offset);
+  AppendU64(bytes, payload.size());
+  AppendU64(bytes, id.size());
+  bytes.insert(bytes.end(), id.bytes_begin(), id.bytes_end());
+  bytes.insert(bytes.end(), payload.begin(), payload.end());
+  return bytes;
+}
+
+struct BundleInput {
+  llvm::StringRef id;
+  llvm::ArrayRef<uint8_t> payload;
+};
+
+struct BundleData {
+  std::vector<uint8_t> bytes;
+  std::vector<uint64_t> entry_offsets;
+};
+
+BundleData MakeBundle(llvm::ArrayRef<BundleInput> inputs) {
+  BundleData result;
+  result.bytes.insert(result.bytes.end(), BundleMagic.bytes_begin(),
+                      BundleMagic.bytes_end());
+  AppendU64(result.bytes, inputs.size());
+
+  uint64_t payload_offset = BundleMagic.size() + sizeof(uint64_t);
+  for (const BundleInput &input : inputs)
+    payload_offset += 3 * sizeof(uint64_t) + input.id.size();
+
+  for (const BundleInput &input : inputs) {
+    result.entry_offsets.push_back(payload_offset);
+    AppendU64(result.bytes, payload_offset);
+    AppendU64(result.bytes, input.payload.size());
+    AppendU64(result.bytes, input.id.size());
+    result.bytes.insert(result.bytes.end(), input.id.bytes_begin(),
+                        input.id.bytes_end());
+    payload_offset += input.payload.size();
+  }
+
+  for (const BundleInput &input : inputs)
+    result.bytes.insert(result.bytes.end(), input.payload.begin(),
+                        input.payload.end());
+  return result;
+}
+
+llvm::Expected<TestFile> MakeGPUELF(llvm::StringRef elf_flag) {
+  std::string yaml = R"(
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  OSABI:           ELFOSABI_AMDGPU_HSA
+  ABIVersion:      0x1
+  Type:            ET_DYN
+  Machine:         EM_AMDGPU
+  Flags:           [ )" +
+                     elf_flag.str() + R"( ]
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x2000
+    AddressAlign:    0x4
+    Content:         '00000000'
+...
+)";
+  return TestFile::fromYaml(yaml);
+}
+
+llvm::Expected<TestFile>
+MakeELFContainingBundle(llvm::ArrayRef<uint8_t> bundle) {
+  std::string yaml = "--- !ELF\n"
+                     "FileHeader:\n"
+                     "  Class:           ELFCLASS64\n"
+                     "  Data:            ELFDATA2LSB\n"
+                     "  Type:            ET_DYN\n"
+                     "  Machine:         EM_X86_64\n"
+                     "Sections:\n"
+                     "  - Name:            .hip_fatbin\n"
+                     "    Type:            SHT_PROGBITS\n"
+                     "    Offset:          0x1000\n"
+                     "    AddressAlign:    0x10\n"
+                     "    Content:         " +
+                     llvm::toHex(bundle) + "\n...\n";
+  return TestFile::fromYaml(yaml);
+}
+
+llvm::Expected<TestFile> MakeELFWithBundle(llvm::StringRef id,
+                                           uint64_t &entry_offset,
+                                           uint64_t &entry_size) {
+  auto inner_file = MakeGPUELF("EF_AMDGPU_MACH_AMDGCN_GFX942");
+  if (!inner_file)
+    return inner_file.takeError();
+
+  DataExtractorSP inner_data = inner_file->moduleSpec().GetExtractor();
+  llvm::ArrayRef<uint8_t> inner_bytes(inner_data->GetDataStart(),
+                                      inner_data->GetByteSize());
+  std::vector<uint8_t> bundle = MakeBundle(id, inner_bytes);
+
+  entry_offset = BundleSectionOffset + BundleMagic.size() +
+                 4 * sizeof(uint64_t) + id.size();
+  entry_size = inner_bytes.size();
+  return MakeELFContainingBundle(bundle);
+}
+
+class ObjectContainerClangOffloadBundleTest : public ::testing::Test {
+  SubsystemRAII<FileSystem, HostInfo, ObjectFileELF,
+                ObjectContainerClangOffloadBundle>
+      subsystems;
+};
+
+} // namespace
+
+TEST_F(ObjectContainerClangOffloadBundleTest, FindsAndLoadsDeviceImage) {
+  constexpr llvm::StringLiteral BundleID = "hipv4-amdgpu-amd-amdhsa--gfx942";
+  uint64_t entry_offset;
+  uint64_t entry_size;
+  auto bundled_file = MakeELFWithBundle(BundleID, entry_offset, entry_size);
+  ASSERT_THAT_EXPECTED(bundled_file, llvm::Succeeded());
+
+  llvm::Expected<llvm::sys::fs::TempFile> temp_file =
+      bundled_file->writeToTemporaryFile();
+  ASSERT_THAT_EXPECTED(temp_file, llvm::Succeeded());
+  const std::string path = temp_file->TmpName;
+  llvm::FileRemover file_remover(path);
+  ASSERT_THAT_ERROR(temp_file->keep(), llvm::Succeeded());
+  FileSpec file(path);
+
+  ModuleSpec input_spec = bundled_file->moduleSpec();
+  DataExtractorSP data = input_spec.GetExtractor();
+
+  ModuleSpecList device_specs =
+      ObjectContainerClangOffloadBundle::GetModuleSpecifications(
+          file, data, /*file_offset=*/0, data->GetByteSize());
+  ASSERT_EQ(device_specs.GetSize(), 1u);
+
+  ModuleSpec device_spec;
+  ASSERT_TRUE(device_specs.GetModuleSpecAtIndex(0, device_spec));
+  EXPECT_EQ(device_spec.GetObjectOffset(), entry_offset);
+  EXPECT_EQ(device_spec.GetObjectSize(), entry_size);
+  EXPECT_EQ(device_spec.GetArchitecture().GetClangTargetCPU(), "gfx942");
+
+  ModuleSpecList all_specs =
+      ObjectFile::GetModuleSpecifications(file, /*file_offset=*/0,
+                                          /*file_size=*/0);
+  ASSERT_EQ(all_specs.GetSize(), 2u);
+
+  ModuleSpec requested(file, device_spec.GetArchitecture());
+  auto module_sp = std::make_shared<Module>(requested);
+  EXPECT_EQ(module_sp->GetObjectOffset(), entry_offset);
+  ASSERT_NE(module_sp->GetObjectFile(), nullptr);
+  EXPECT_EQ(module_sp->GetObjectFile()->GetArchitecture().GetClangTargetCPU(),
+            "gfx942");
+
+  auto container_module_sp =
+      std::make_shared<Module>(file, device_spec.GetArchitecture());
+  ASSERT_NE(container_module_sp->GetObjectFile(), nullptr);
+  EXPECT_EQ(container_module_sp->GetObjectFile()->GetFileOffset(),
+            entry_offset);
+
+  constexpr lldb::offset_t containing_object_offset = 0x80;
+  ModuleSpecList nested_specs =
+      ObjectContainerClangOffloadBundle::GetModuleSpecifications(
+          FileSpec(), data, containing_object_offset, data->GetByteSize());
+  ASSERT_EQ(nested_specs.GetSize(), 1u);
+  ModuleSpec nested_spec;
+  ASSERT_TRUE(nested_specs.GetModuleSpecAtIndex(0, nested_spec));
+  EXPECT_EQ(nested_spec.GetObjectOffset(),
+            containing_object_offset + entry_offset);
+}
+
+TEST_F(ObjectContainerClangOffloadBundleTest,
+       FindsAndLoadsCompressedDeviceImage) {
+  if (!llvm::compression::zlib::isAvailable())
+    GTEST_SKIP() << "zlib is unavailable";
+
+  auto gpu_file = MakeGPUELF("EF_AMDGPU_MACH_AMDGCN_GFX942");
+  ASSERT_THAT_EXPECTED(gpu_file, llvm::Succeeded());
+  DataExtractorSP gpu_data = gpu_file->moduleSpec().GetExtractor();
+  llvm::ArrayRef<uint8_t> gpu_bytes(gpu_data->GetDataStart(),
+                                    gpu_data->GetByteSize());
+
+  std::vector<uint8_t> bundle =
+      MakeBundle("hipv4-amdgpu-amd-amdhsa--gfx942", gpu_bytes);
+  auto bundle_buffer = llvm::MemoryBuffer::getMemBufferCopy(llvm::StringRef(
+      reinterpret_cast<const char *>(bundle.data()), bundle.size()));
+  auto compressed = llvm::object::CompressedOffloadBundle::compress(
+      llvm::compression::Params(llvm::compression::Format::Zlib),
+      *bundle_buffer, llvm::object::CompressedOffloadBundle::DefaultVersion);
+  ASSERT_THAT_EXPECTED(compressed, llvm::Succeeded());
+
+  llvm::StringRef compressed_bytes = (*compressed)->getBuffer();
+  auto bundled_file = MakeELFContainingBundle(llvm::ArrayRef<uint8_t>(
+      reinterpret_cast<const uint8_t *>(compressed_bytes.data()),
+      compressed_bytes.size()));
+  ASSERT_THAT_EXPECTED(bundled_file, llvm::Succeeded());
+
+  auto temp_file = bundled_file->writeToTemporaryFile();
+  ASSERT_THAT_EXPECTED(temp_file, llvm::Succeeded());
+  const std::string path = temp_file->TmpName;
+  llvm::FileRemover file_remover(path);
+  ASSERT_THAT_ERROR(temp_file->keep(), llvm::Succeeded());
+  FileSpec file(path);
+
+  ModuleSpecList all_specs =
+      ObjectFile::GetModuleSpecifications(file, /*file_offset=*/0,
+                                          /*file_size=*/0);
+  ASSERT_EQ(all_specs.GetSize(), 2u);
+
+  ModuleSpec device_spec;
+  ASSERT_TRUE(all_specs.GetModuleSpecAtIndex(1, device_spec));
+  EXPECT_EQ(device_spec.GetObjectOffset(), 0u);
+  EXPECT_EQ(device_spec.GetObjectSize(), gpu_bytes.size());
+  ASSERT_NE(device_spec.GetExtractor(), nullptr);
+  EXPECT_EQ(device_spec.GetArchitecture().GetClangTargetCPU(), "gfx942");
+
+  auto module_sp = std::make_shared<Module>(device_spec);
+  ASSERT_NE(module_sp->GetObjectFile(), nullptr);
+  EXPECT_EQ(module_sp->GetObjectFile()->GetArchitecture().GetClangTargetCPU(),
+            "gfx942");
+
+  ModuleSpec requested(file, device_spec.GetArchitecture());
+  auto container_module_sp = std::make_shared<Module>(requested);
+  ASSERT_NE(container_module_sp->GetObjectFile(), nullptr);
+  EXPECT_EQ(container_module_sp->GetObjectFile()
+                ->GetArchitecture()
+                .GetClangTargetCPU(),
+            "gfx942");
+}
+
+TEST_F(ObjectContainerClangOffloadBundleTest,
+       FindsMultipleDeviceImagesAndSkipsEmptyHostImage) {
+  auto gfx908_file = MakeGPUELF("EF_AMDGPU_MACH_AMDGCN_GFX908");
+  ASSERT_THAT_EXPECTED(gfx908_file, llvm::Succeeded());
+  auto gfx942_file = MakeGPUELF("EF_AMDGPU_MACH_AMDGCN_GFX942");
+  ASSERT_THAT_EXPECTED(gfx942_file, llvm::Succeeded());
+
+  DataExtractorSP gfx908_data = gfx908_file->moduleSpec().GetExtractor();
+  DataExtractorSP gfx942_data = gfx942_file->moduleSpec().GetExtractor();
+  std::array<BundleInput, 3> inputs = {{
+      {"host-x86_64-unknown-linux-gnu", {}},
+      {"hipv4-amdgpu-amd-amdhsa--gfx908",
+       {gfx908_data->GetDataStart(), gfx908_data->GetByteSize()}},
+      {"hipv4-amdgpu-amd-amdhsa--gfx942",
+       {gfx942_data->GetDataStart(), gfx942_data->GetByteSize()}},
+  }};
+  BundleData bundle = MakeBundle(inputs);
+  auto bundled_file = MakeELFContainingBundle(bundle.bytes);
+  ASSERT_THAT_EXPECTED(bundled_file, llvm::Succeeded());
+
+  auto temp_file = bundled_file->writeToTemporaryFile();
+  ASSERT_THAT_EXPECTED(temp_file, llvm::Succeeded());
+  const std::string path = temp_file->TmpName;
+  llvm::FileRemover file_remover(path);
+  ASSERT_THAT_ERROR(temp_file->keep(), llvm::Succeeded());
+  FileSpec file(path);
+
+  ModuleSpec input_spec = bundled_file->moduleSpec();
+  DataExtractorSP data = input_spec.GetExtractor();
+  ModuleSpecList device_specs =
+      ObjectContainerClangOffloadBundle::GetModuleSpecifications(
+          file, data, /*file_offset=*/0, data->GetByteSize());
+  ASSERT_EQ(device_specs.GetSize(), 2u);
+
+  ModuleSpec gfx908_spec;
+  ModuleSpec gfx942_spec;
+  ASSERT_TRUE(device_specs.GetModuleSpecAtIndex(0, gfx908_spec));
+  ASSERT_TRUE(device_specs.GetModuleSpecAtIndex(1, gfx942_spec));
+  EXPECT_EQ(gfx908_spec.GetArchitecture().GetClangTargetCPU(), "gfx908");
+  EXPECT_EQ(gfx942_spec.GetArchitecture().GetClangTargetCPU(), "gfx942");
+  EXPECT_EQ(gfx908_spec.GetObjectOffset(),
+            BundleSectionOffset + bundle.entry_offsets[1]);
+  EXPECT_EQ(gfx942_spec.GetObjectOffset(),
+            BundleSectionOffset + bundle.entry_offsets[2]);
+  EXPECT_EQ(gfx908_spec.GetObjectSize(), gfx908_data->GetByteSize());
+  EXPECT_EQ(gfx942_spec.GetObjectSize(), gfx942_data->GetByteSize());
+
+  ModuleSpecList all_specs =
+      ObjectFile::GetModuleSpecifications(file, /*file_offset=*/0,
+                                          /*file_size=*/0);
+  EXPECT_EQ(all_specs.GetSize(), 3u);
+
+  ModuleSpec requested(file, gfx942_spec.GetArchitecture());
+  auto module_sp = std::make_shared<Module>(requested);
+  ASSERT_NE(module_sp->GetObjectFile(), nullptr);
+  EXPECT_EQ(module_sp->GetObjectFile()->GetArchitecture().GetClangTargetCPU(),
+            "gfx942");
+  EXPECT_EQ(module_sp->GetObjectFile()->GetFileOffset(),
+            gfx942_spec.GetObjectOffset());
+}
+
+TEST_F(ObjectContainerClangOffloadBundleTest, PlainELFHasOneSpecification) {
+  auto plain_file = TestFile::fromYaml(R"(
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_DYN
+  Machine:         EM_X86_64
+...
+)");
+  ASSERT_THAT_EXPECTED(plain_file, llvm::Succeeded());
+
+  ModuleSpec spec = plain_file->moduleSpec();
+  DataExtractorSP data = spec.GetExtractor();
+  ModuleSpecList specs = ObjectFile::GetModuleSpecifications(
+      FileSpec(), data, /*file_offset=*/0, data->GetByteSize());
+  EXPECT_EQ(specs.GetSize(), 1u);
+}

>From 9a6be77eee2bbc8c4be28d48bf4d07b117e19fe2 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 targets such as GPUs do not use a rendezvous breakpoint in the same 
way as SVR4 loaders. Their runtime instead tells the lldb-server accelerator 
plugin when libraries load or unload.

Add a DynamicLoader that queries jAcceleratorPluginGetDynamicLoaderLibraryInfo 
and applies the returned module loads. The loader handles whole-file paths and 
containing files with reported offset and size metadata. When a file contains a 
Clang offload bundle, the target architecture selects its device image.

Select the loader for AMDGPU and NVPTX targets backed by ProcessGDBRemote. 
Extend the mock accelerator process to serve the packet on an accelerator 
connection.

Test whole-file loading, loading an AMDGPU image from a real Clang offload 
bundle inside a host ELF, and avoiding this loader for host targets.
---
 .../lldb/Host/common/NativeProcessProtocol.h  |   9 +
 .../AcceleratorGDBRemote/CMakeLists.txt       |  13 ++
 .../DynamicLoaderAcceleratorGDBRemote.cpp     | 158 +++++++++++++
 .../DynamicLoaderAcceleratorGDBRemote.h       |  46 ++++
 .../Plugins/DynamicLoader/CMakeLists.txt      |   1 +
 .../GDBRemoteCommunicationClient.cpp          |  23 ++
 .../gdb-remote/GDBRemoteCommunicationClient.h |   5 +
 .../GDBRemoteCommunicationServerLLGS.cpp      |  43 ++--
 .../TestAcceleratorDynamicLoader.py           | 211 ++++++++++++++++++
 .../dynamic_loader/accelerator.yaml           |  23 ++
 .../dynamic_loader/accelerator_lib.yaml       |  23 ++
 .../dynamic_loader/embedded_lib.yaml          |  23 ++
 .../API/accelerator/dynamic_loader/host.yaml  |  20 ++
 .../Mock/ProcessMockAccelerator.cpp           |  12 +
 .../Accelerator/Mock/ProcessMockAccelerator.h |   4 +
 15 files changed, 600 insertions(+), 14 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 58c91bcd84a3b..15ca3c32481e8 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/AddressSpace.h"
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/Iterable.h"
@@ -170,6 +171,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..4c16c933b477a
--- /dev/null
+++ 
b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.cpp
@@ -0,0 +1,158 @@
+//===----------------------------------------------------------------------===//
+//
+// 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);
+
+  // CreateInstance ensures that m_process is 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);
+    // Select the target device image when the file contains an offload bundle.
+    module_spec.GetArchitecture() = target.GetArchitecture();
+    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..b5080322ed714
--- /dev/null
+++ 
b/lldb/source/Plugins/DynamicLoader/AcceleratorGDBRemote/DynamicLoaderAcceleratorGDBRemote.h
@@ -0,0 +1,46 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 53665cd409739..bda489a0231ea 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.cpp
@@ -306,6 +306,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 dd612177ad45a..3bf34bf8cc06e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationClient.h
@@ -389,6 +389,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 38aeb1bb40903..2d81684968e56 100644
--- 
a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ 
b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -4716,20 +4716,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)
-        return SendErrorResponse(
-            Status::FromErrorString("no dynamic loader info available"));
-
-      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)
+          return SendErrorResponse(
+              Status::FromErrorString("no dynamic loader info available"));
+
+        StreamGDBRemote stream;
+        stream.PutAsJSON(*response, /*hex_ascii=*/false);
+        return SendPacketNoLock(stream.GetString());
+      }
     }
+    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..bcda92a4e7ac8
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/TestAcceleratorDynamicLoader.py
@@ -0,0 +1,211 @@
+"""
+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 struct
+
+import lldb
+from lldbsuite.test.decorators import *
+from lldbsuite.test.gdbclientutils import *
+from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase
+
+DYLD_PACKET = "jAcceleratorPluginGetDynamicLoaderLibraryInfo:"
+BUNDLE_MAGIC = b"__CLANG_OFFLOAD_BUNDLE__"
+BUNDLE_SECTION_OFFSET = 0x2000
+
+CONTAINER_YAML = """\
+--- !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"
+  - Name:            .hip_fatbin
+    Type:            SHT_PROGBITS
+    Offset:          0x2000
+    AddressAlign:    0x1000
+    Content:         "%s"
+"""
+
+
+def make_clang_offload_bundle(entries):
+    """Return a legacy Clang offload bundle and its entry offsets."""
+    ids = [entry_id.encode("utf-8") for entry_id, _ in entries]
+    payload_offset = len(BUNDLE_MAGIC) + 8
+    payload_offset += sum(24 + len(entry_id) for entry_id in ids)
+
+    descriptors = bytearray()
+    payloads = bytearray()
+    offsets = []
+    for entry_id, (_, payload) in zip(ids, entries):
+        offsets.append(payload_offset)
+        descriptors.extend(
+            struct.pack("<QQQ", payload_offset, len(payload), len(entry_id))
+        )
+        descriptors.extend(entry_id)
+        payloads.extend(payload)
+        payload_offset += len(payload)
+
+    return (
+        BUNDLE_MAGIC + struct.pack("<Q", len(entries)) + descriptors + 
payloads,
+        offsets,
+    )
+
+
+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):
+        """Embed a library in a Clang offload bundle inside a host ELF.
+
+        Returns the container path and the (offset, size) of the device object.
+
+        The embedded object puts .text at a different address than the outer
+        one, making it observable whether LLDB selected the bundled device
+        image or the host ELF.
+        """
+        embedded_path = self.getBuildArtifact("embedded_lib.so")
+        self.yaml2obj("embedded_lib.yaml", embedded_path)
+        with open(embedded_path, "rb") as f:
+            embedded_bytes = f.read()
+
+        bundle, offsets = make_clang_offload_bundle(
+            [
+                ("host-x86_64-unknown-linux--", b""),
+                ("hipv4-amdgpu-amd-amdhsa--gfx942", embedded_bytes),
+            ]
+        )
+        yaml_path = self.getBuildArtifact("container.yaml")
+        with open(yaml_path, "w") as f:
+            f.write(CONTAINER_YAML % bundle.hex())
+        container_path = self.getBuildArtifact("container.bin")
+        self.yaml2obj(yaml_path, container_path)
+        offset = BUNDLE_SECTION_OFFSET + offsets[1]
+        with open(container_path, "rb") as f:
+            container_bytes = f.read()
+        self.assertEqual(
+            container_bytes[offset : offset + len(embedded_bytes)], 
embedded_bytes
+        )
+        return container_path, offset, 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 Clang bundle is loaded from the host ELF."""
+        container, offset, size = self.make_container()
+        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 bundled device object has .text at 0x3000; the host has it at
+        # 0x1000.
+        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..2c4fe0fcd752c
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/accelerator.yaml
@@ -0,0 +1,23 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_EXEC
+  Machine:         EM_AMDGPU
+  OSABI:           ELFOSABI_AMDGPU_HSA
+  ABIVersion:      0x1
+  Flags:           [ EF_AMDGPU_MACH_AMDGCN_GFX942 ]
+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..ccdd8b34aa929
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/accelerator_lib.yaml
@@ -0,0 +1,23 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_DYN
+  Machine:         EM_AMDGPU
+  OSABI:           ELFOSABI_AMDGPU_HSA
+  ABIVersion:      0x1
+  Flags:           [ EF_AMDGPU_MACH_AMDGCN_GFX942 ]
+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..cb89940712ae3
--- /dev/null
+++ b/lldb/test/API/accelerator/dynamic_loader/embedded_lib.yaml
@@ -0,0 +1,23 @@
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  Type:            ET_DYN
+  Machine:         EM_AMDGPU
+  OSABI:           ELFOSABI_AMDGPU_HSA
+  ABIVersion:      0x1
+  Flags:           [ EF_AMDGPU_MACH_AMDGCN_GFX942 ]
+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 f179b9c6b8781..4a56f488e9964 100644
--- a/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
+++ b/lldb/tools/lldb-server/Plugins/Accelerator/Mock/ProcessMockAccelerator.cpp
@@ -112,3 +112,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 7245e4aeca5d5..60c095a80eba9 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