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

>From ebddb567da025b756fa995aa51eefc257ac4f4c5 Mon Sep 17 00:00:00 2001
From: satya janga <[email protected]>
Date: Tue, 8 Sep 2026 14:46:31 -0700
Subject: [PATCH] [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     | 246 ++++++++++++
 .../ObjectContainerClangOffloadBundle.h       |  80 ++++
 lldb/source/Symbol/ObjectFile.cpp             |  21 +-
 lldb/unittests/ObjectContainer/CMakeLists.txt |   3 +
 .../ObjectContainerClangOffloadBundleTest.cpp | 360 ++++++++++++++++++
 7 files changed, 717 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..4dea443cc0af4
--- /dev/null
+++ 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
@@ -0,0 +1,246 @@
+//===----------------------------------------------------------------------===//
+//
+// 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) {
+      const bool matches = pass == 0 ? arch.IsExactMatch(entry.arch)
+                                     : arch.IsCompatibleMatch(entry.arch);
+      if (!matches)
+        continue;
+
+      DataExtractorSP extractor_sp = entry.extractor_sp;
+      lldb::offset_t data_offset = 0;
+      if (ObjectFileSP object_file_sp =
+              ObjectFile::FindPlugin(module_sp, file, entry.offset, entry.size,
+                                     extractor_sp, data_offset))
+        return object_file_sp;
+    }
+  }
+
+  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 8acfccf12f4dd..4df5d9482547c 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
     lldbCore
     lldbUtilityHelpers
     LLVMTestingSupport
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);
+}

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

Reply via email to