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

>From 5d25dcdc3de13eedefda1b6e995d2fcbeac54385 Mon Sep 17 00:00:00 2001
From: Greg Clayton <[email protected]>
Date: Fri, 15 May 2026 18:13:51 -0700
Subject: [PATCH 1/4] Add an ObjectContainer plug-in for clang offload bundles.

Clang supports embedding binaries using the clang offload bundler:

https://clang.llvm.org/docs/ClangOffloadBundler.html

This patch creates an ObjectContainer plug-in that allows us to get the 
contained binaries within any executable. Legacy amdgcn bundle IDs are 
canonicalized locally for current upstream LLVM.

(cherry picked from commit 4b255f4318cac2b7e892d427bd7b765f4730dc9e)
---
 .../Plugins/ObjectContainer/CMakeLists.txt    |   1 +
 .../Clang-Offload-Bundle/CMakeLists.txt       |  13 ++
 .../ObjectContainerClangOffloadBundle.cpp     | 206 ++++++++++++++++++
 .../ObjectContainerClangOffloadBundle.h       |  75 +++++++
 lldb/source/Symbol/ObjectFile.cpp             |  21 +-
 5 files changed, 309 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

diff --git a/lldb/source/Plugins/ObjectContainer/CMakeLists.txt 
b/lldb/source/Plugins/ObjectContainer/CMakeLists.txt
index 220a76d1159d5..636d0b15ca9d3 100644
--- a/lldb/source/Plugins/ObjectContainer/CMakeLists.txt
+++ b/lldb/source/Plugins/ObjectContainer/CMakeLists.txt
@@ -4,3 +4,4 @@ add_subdirectory(BSD-Archive)
 add_subdirectory(Big-Archive) 
 add_subdirectory(Universal-Mach-O)
 add_subdirectory(Mach-O-Fileset)
+add_subdirectory(Clang-Offload-Bundle)
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..4794280477786
--- /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
+    Object
+    BinaryFormat
+  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..f0c73f5b6ad7a
--- /dev/null
+++ 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
@@ -0,0 +1,206 @@
+//===-- ObjectContainerClangOffloadBundle.cpp
+//------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "ObjectContainerClangOffloadBundle.h"
+#include "lldb/Core/Module.h"
+#include "lldb/Core/ModuleSpec.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Symbol/ObjectFile.h"
+#include "lldb/Target/Target.h"
+#include "lldb/Utility/ArchSpec.h"
+#include "lldb/Utility/DataBuffer.h"
+#include "llvm/BinaryFormat/Magic.h"
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Object/OffloadBundle.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(ObjectContainerClangOffloadBundle)
+
+void ObjectContainerClangOffloadBundle::Initialize() {
+  PluginManager::RegisterPlugin(GetPluginNameStatic(),
+                                GetPluginDescriptionStatic(), CreateInstance,
+                                GetModuleSpecifications, 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());
+  llvm::file_magic magic = llvm::identify_magic(bytes);
+  switch (magic) {
+  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-amdgcn-amd-amdhsa--gfx906", "host-x86_64-unknown-linux-gnu"
+  auto [Kind, Triple] = ID.split('-');
+  if (Triple.empty())
+    return ArchSpec();
+
+  // LLVM now uses "amdgpu" as the canonical spelling for the legacy
+  // "amdgcn" architecture name used in offload bundle IDs.
+  if (Triple.consume_front("amdgcn-")) {
+    std::string canonical_triple = "amdgpu-";
+    canonical_triple.append(Triple);
+    return ArchSpec(canonical_triple);
+  }
+  return ArchSpec(Triple);
+}
+
+bool ObjectContainerClangOffloadBundle::FindBundleEntries(
+    const FileSpec &file, std::vector<Entry> &entries) {
+  std::string path = file.GetPath();
+  if (path.empty())
+    return false;
+
+  auto obj_or_err = llvm::object::ObjectFile::createObjectFile(path);
+  if (!obj_or_err) {
+    llvm::consumeError(obj_or_err.takeError());
+    return false;
+  }
+
+  llvm::SmallVector<llvm::object::OffloadBundleFatBin> bundles;
+  if (auto err = llvm::object::extractOffloadBundleFatBinary(
+          *obj_or_err->getBinary(), bundles)) {
+    llvm::consumeError(std::move(err));
+    return false;
+  }
+
+  if (bundles.empty())
+    return false;
+
+  for (auto &bundle : bundles) {
+    for (auto &bundle_entry : bundle.getEntries()) {
+      if (bundle_entry.Size == 0)
+        continue;
+      Entry entry;
+      entry.arch = ParseArchFromBundleEntryID(bundle_entry.ID);
+      entry.offset = bundle_entry.Offset;
+      entry.size = bundle_entry.Size;
+      if (entry.arch.IsValid())
+        entries.push_back(std::move(entry));
+    }
+  }
+
+  return !entries.empty();
+}
+
+ObjectContainer *ObjectContainerClangOffloadBundle::CreateInstance(
+    const lldb::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();
+  if (!FindBundleEntries(m_file, m_entries))
+    return false;
+  return true;
+}
+
+size_t ObjectContainerClangOffloadBundle::GetNumArchitectures() const {
+  return m_entries.size();
+}
+
+bool ObjectContainerClangOffloadBundle::GetArchitectureAtIndex(
+    uint32_t idx, ArchSpec &arch) const {
+  if (idx < m_entries.size()) {
+    arch = m_entries[idx].arch;
+    return true;
+  }
+  return false;
+}
+
+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, entries))
+    return {};
+
+  ModuleSpecList specs;
+  for (const Entry &entry : entries) {
+    ModuleSpec spec(file, 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;
+        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..79b416b9a63ab
--- /dev/null
+++ 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.h
@@ -0,0 +1,75 @@
+//===-- ObjectContainerClangOffloadBundle.h ----------------------*- C++
+//-*-===//
+//
+// 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.";
+  }
+
+  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;
+  };
+  std::vector<Entry> m_entries;
+
+  static bool FindBundleEntries(const lldb_private::FileSpec &file,
+                                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,

>From a80a97e1cd90044e3f6ea0a8051c237fd1976b56 Mon Sep 17 00:00:00 2001
From: Bar Soloveychik <[email protected]>
Date: Thu, 18 Jun 2026 13:24:50 -0700
Subject: [PATCH 2/4] [lldb] Cache clang offload bundle entries per file (#111)

* [lldb] Cache clang offload bundle entries per file

ObjectContainerClangOffloadBundle::FindBundleEntries() re-opened the file and 
re-scanned its offload bundle on every call. A single fat binary can embed 
hundreds of device code objects, each loaded as its own module, so the same 
bundle was parsed once per embedded object.

Cache the parsed entries per file (keyed by path, size and mtime, guarded by a 
mutex since modules can load concurrently) so the bundle is scanned only once. 
Pure performance change; the entries returned are identical. On a large GPU 
core this cut module loading from ~45s to ~33s with no change in symbolication.

* Address comments

---------

Co-authored-by: Bar Soloveychik <[email protected]>
(cherry picked from commit 5bf13a9107f462a12959251161cd89a3e4c27a04)
---
 .../ObjectContainerClangOffloadBundle.cpp     | 85 +++++++++++++------
 1 file changed, 61 insertions(+), 24 deletions(-)

diff --git 
a/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
index f0c73f5b6ad7a..2cc34bc98ed36 100644
--- 
a/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
+++ 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
@@ -1,5 +1,4 @@
-//===-- ObjectContainerClangOffloadBundle.cpp
-//------------------------------===//
+//===----------------------------------------------------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM 
Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -11,13 +10,17 @@
 #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/ArchSpec.h"
 #include "lldb/Utility/DataBuffer.h"
+#include "llvm/ADT/StringMap.h"
 #include "llvm/BinaryFormat/Magic.h"
 #include "llvm/Object/ObjectFile.h"
 #include "llvm/Object/OffloadBundle.h"
+#include "llvm/Support/Chrono.h"
+#include <mutex>
 
 using namespace lldb;
 using namespace lldb_private;
@@ -84,35 +87,69 @@ bool ObjectContainerClangOffloadBundle::FindBundleEntries(
   if (path.empty())
     return false;
 
-  auto obj_or_err = llvm::object::ObjectFile::createObjectFile(path);
-  if (!obj_or_err) {
-    llvm::consumeError(obj_or_err.takeError());
-    return false;
+  // Cache the parse per file so a bundle with many code objects isn't 
rescanned
+  // once per object. Keyed by path and validated by mtime, so a changed file
+  // re-parses and overwrites. Locked for concurrent module loads.
+  struct CacheValue {
+    llvm::sys::TimePoint<> mod_time;
+    std::vector<Entry> entries;
+  };
+  static std::mutex cache_mutex;
+  static llvm::StringMap<CacheValue> cache;
+
+  llvm::sys::TimePoint<> mod_time =
+      FileSystem::Instance().GetModificationTime(file);
+
+  {
+    std::lock_guard<std::mutex> lock(cache_mutex);
+    auto it = cache.find(path);
+    if (it != cache.end() && it->second.mod_time == mod_time) {
+      entries = it->second.entries;
+      return !entries.empty();
+    }
   }
 
-  llvm::SmallVector<llvm::object::OffloadBundleFatBin> bundles;
-  if (auto err = llvm::object::extractOffloadBundleFatBinary(
-          *obj_or_err->getBinary(), bundles)) {
-    llvm::consumeError(std::move(err));
-    return false;
-  }
+  // Parse the offload bundle (helper keeps this separate from the caching).
+  auto parse = [&path]() -> std::vector<Entry> {
+    std::vector<Entry> result;
+    auto obj_or_err = llvm::object::ObjectFile::createObjectFile(path);
+    if (!obj_or_err) {
+      llvm::consumeError(obj_or_err.takeError());
+      return result;
+    }
 
-  if (bundles.empty())
-    return false;
+    llvm::SmallVector<llvm::object::OffloadBundleFatBin> bundles;
+    if (auto err = llvm::object::extractOffloadBundleFatBinary(
+            *obj_or_err->getBinary(), bundles)) {
+      llvm::consumeError(std::move(err));
+      return result;
+    }
 
-  for (auto &bundle : bundles) {
-    for (auto &bundle_entry : bundle.getEntries()) {
-      if (bundle_entry.Size == 0)
-        continue;
-      Entry entry;
-      entry.arch = ParseArchFromBundleEntryID(bundle_entry.ID);
-      entry.offset = bundle_entry.Offset;
-      entry.size = bundle_entry.Size;
-      if (entry.arch.IsValid())
-        entries.push_back(std::move(entry));
+    for (auto &bundle : bundles) {
+      for (auto &bundle_entry : bundle.getEntries()) {
+        if (bundle_entry.Size == 0)
+          continue;
+        Entry entry;
+        entry.arch = ParseArchFromBundleEntryID(bundle_entry.ID);
+        entry.offset = bundle_entry.Offset;
+        entry.size = bundle_entry.Size;
+        if (entry.arch.IsValid())
+          result.push_back(std::move(entry));
+      }
     }
+    return result;
+  };
+
+  std::vector<Entry> parsed = parse();
+
+  // Store/overwrite this path's entry; cache empty results too so non-bundle
+  // files aren't re-parsed.
+  {
+    std::lock_guard<std::mutex> lock(cache_mutex);
+    cache[path] = CacheValue{mod_time, parsed};
   }
 
+  entries = std::move(parsed);
   return !entries.empty();
 }
 

>From 40c37e7ddae63bdd23f606b337a6d95a6e478b13 Mon Sep 17 00:00:00 2001
From: satya janga <[email protected]>
Date: Wed, 9 Sep 2026 13:17:50 -0700
Subject: [PATCH 3/4] [lldb] Load compressed Clang offload bundle entries

Compressed bundle entry offsets refer to the decompressed bundle rather than 
the containing file. Keep an owned buffer for each decompressed object, attach 
it to its module specification, and use it when creating the object file.

Add a zlib-compressed hipv4 bundle regression test.

(cherry picked from commit 8996869896a4910315ab2396033297bbbd61aea7)
---
 .../ObjectContainerClangOffloadBundle.cpp     |  32 +++-
 .../ObjectContainerClangOffloadBundle.h       |   1 +
 lldb/unittests/ObjectContainer/CMakeLists.txt |   3 +
 .../ObjectContainerClangOffloadBundleTest.cpp | 160 ++++++++++++++++++
 4 files changed, 193 insertions(+), 3 deletions(-)
 create mode 100644 
lldb/unittests/ObjectContainer/ObjectContainerClangOffloadBundleTest.cpp

diff --git 
a/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
index 2cc34bc98ed36..4f119e2a19ee3 100644
--- 
a/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
+++ 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp
@@ -15,6 +15,7 @@
 #include "lldb/Target/Target.h"
 #include "lldb/Utility/ArchSpec.h"
 #include "lldb/Utility/DataBuffer.h"
+#include "lldb/Utility/DataBufferHeap.h"
 #include "llvm/ADT/StringMap.h"
 #include "llvm/BinaryFormat/Magic.h"
 #include "llvm/Object/ObjectFile.h"
@@ -129,10 +130,31 @@ bool ObjectContainerClangOffloadBundle::FindBundleEntries(
       for (auto &bundle_entry : bundle.getEntries()) {
         if (bundle_entry.Size == 0)
           continue;
+
+        DataExtractorSP entry_extractor_sp;
+        uint64_t offset = bundle_entry.Offset;
+        if (bundle.isDecompressed()) {
+          if (!bundle.DecompressedBuffer)
+            continue;
+
+          llvm::StringRef decompressed = 
bundle.DecompressedBuffer->getBuffer();
+          if (offset > decompressed.size() ||
+              bundle_entry.Size > decompressed.size() - offset)
+            continue;
+
+          auto entry_data_sp = std::make_shared<DataBufferHeap>(
+              decompressed.data() + offset, bundle_entry.Size);
+          entry_extractor_sp = std::make_shared<DataExtractor>(entry_data_sp);
+          // Compressed entries have no corresponding offset in the containing
+          // file. The owned buffer above contains only the selected object.
+          offset = 0;
+        }
+
         Entry entry;
         entry.arch = ParseArchFromBundleEntryID(bundle_entry.ID);
-        entry.offset = bundle_entry.Offset;
+        entry.offset = offset;
         entry.size = bundle_entry.Size;
+        entry.extractor_sp = std::move(entry_extractor_sp);
         if (entry.arch.IsValid())
           result.push_back(std::move(entry));
       }
@@ -205,7 +227,11 @@ ModuleSpecList 
ObjectContainerClangOffloadBundle::GetModuleSpecifications(
 
   ModuleSpecList specs;
   for (const Entry &entry : entries) {
-    ModuleSpec spec(file, entry.arch);
+    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);
@@ -231,7 +257,7 @@ ObjectContainerClangOffloadBundle::GetObjectFile(const 
FileSpec *file) {
       bool match = (pass == 0) ? arch.IsExactMatch(entry.arch)
                                : arch.IsCompatibleMatch(entry.arch);
       if (match) {
-        DataExtractorSP extractor_sp;
+        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);
diff --git 
a/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.h
 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.h
index 79b416b9a63ab..9db8153450315 100644
--- 
a/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.h
+++ 
b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.h
@@ -65,6 +65,7 @@ class ObjectContainerClangOffloadBundle : public 
lldb_private::ObjectContainer {
     lldb_private::ArchSpec arch;
     uint64_t offset = 0;
     uint64_t size = 0;
+    lldb::DataExtractorSP extractor_sp;
   };
   std::vector<Entry> m_entries;
 
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..ec808071cc72d
--- /dev/null
+++ b/lldb/unittests/ObjectContainer/ObjectContainerClangOffloadBundleTest.cpp
@@ -0,0 +1,160 @@
+//===----------------------------------------------------------------------===//
+//
+// 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/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 <cstdint>
+#include <vector>
+
+using namespace lldb;
+using namespace lldb_private;
+
+namespace {
+
+constexpr llvm::StringLiteral BundleMagic = "__CLANG_OFFLOAD_BUNDLE__";
+constexpr llvm::StringLiteral BundleID = "hipv4-amdgcn-amd-amdhsa--gfx942";
+
+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::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) + BundleID.size();
+  AppendU64(bytes, payload_offset);
+  AppendU64(bytes, payload.size());
+  AppendU64(bytes, BundleID.size());
+  bytes.insert(bytes.end(), BundleID.bytes_begin(), BundleID.bytes_end());
+  bytes.insert(bytes.end(), payload.begin(), payload.end());
+  return bytes;
+}
+
+llvm::Expected<TestFile> MakeGPUELF() {
+  return TestFile::fromYaml(R"(
+--- !ELF
+FileHeader:
+  Class:           ELFCLASS64
+  Data:            ELFDATA2LSB
+  OSABI:           ELFOSABI_AMDGPU_HSA
+  ABIVersion:      0x1
+  Type:            ET_DYN
+  Machine:         EM_AMDGPU
+  Flags:           [ EF_AMDGPU_MACH_AMDGCN_GFX942 ]
+Sections:
+  - Name:            .text
+    Type:            SHT_PROGBITS
+    Flags:           [ SHF_ALLOC, SHF_EXECINSTR ]
+    Address:         0x2000
+    AddressAlign:    0x4
+    Content:         '00000000'
+...
+)");
+}
+
+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);
+}
+
+class ObjectContainerClangOffloadBundleTest : public ::testing::Test {
+  SubsystemRAII<FileSystem, HostInfo, ObjectFileELF,
+                ObjectContainerClangOffloadBundle>
+      subsystems;
+};
+
+} // namespace
+
+TEST_F(ObjectContainerClangOffloadBundleTest, LoadsCompressedDeviceImage) {
+  if (!llvm::compression::zlib::isAvailable())
+    GTEST_SKIP() << "zlib is unavailable";
+
+  auto gpu_file = MakeGPUELF();
+  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(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());
+
+  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);
+
+  ModuleSpecList specs = ObjectFile::GetModuleSpecifications(file, 0, 0);
+  ASSERT_EQ(2u, specs.GetSize());
+
+  ModuleSpec device_spec;
+  ASSERT_TRUE(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);
+  requested.GetArchitecture() = device_spec.GetArchitecture();
+  auto selected_module_sp = std::make_shared<Module>(requested);
+  ASSERT_NE(selected_module_sp->GetObjectFile(), nullptr);
+  EXPECT_EQ(selected_module_sp->GetObjectFile()
+                ->GetArchitecture()
+                .GetClangTargetCPU(),
+            "gfx942");
+}

>From fe3ae4428885aeac9cbaf270e8f80dcaaa8f01f0 Mon Sep 17 00:00:00 2001
From: satya janga <[email protected]>
Date: Sun, 20 Sep 2026 14:02:37 -0700
Subject: [PATCH 4/4] [lldb] Expand Clang offload bundle container tests

Cover uncompressed and multiple device images, empty host entries, container 
selection, and plain ELF files.
---
 .../ObjectContainerClangOffloadBundleTest.cpp | 234 ++++++++++++++++--
 1 file changed, 212 insertions(+), 22 deletions(-)

diff --git 
a/lldb/unittests/ObjectContainer/ObjectContainerClangOffloadBundleTest.cpp 
b/lldb/unittests/ObjectContainer/ObjectContainerClangOffloadBundleTest.cpp
index ec808071cc72d..5b627d18dedaf 100644
--- a/lldb/unittests/ObjectContainer/ObjectContainerClangOffloadBundleTest.cpp
+++ b/lldb/unittests/ObjectContainer/ObjectContainerClangOffloadBundleTest.cpp
@@ -17,6 +17,7 @@
 #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"
@@ -24,7 +25,9 @@
 #include "llvm/Testing/Support/Error.h"
 #include "gtest/gtest.h"
 
+#include <array>
 #include <cstdint>
+#include <string>
 #include <vector>
 
 using namespace lldb;
@@ -32,30 +35,67 @@ using namespace lldb_private;
 
 namespace {
 
+constexpr uint64_t BundleSectionOffset = 0x1000;
 constexpr llvm::StringLiteral BundleMagic = "__CLANG_OFFLOAD_BUNDLE__";
-constexpr llvm::StringLiteral BundleID = "hipv4-amdgcn-amd-amdhsa--gfx942";
 
 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::ArrayRef<uint8_t> payload) {
+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) + BundleID.size();
+      BundleMagic.size() + 4 * sizeof(uint64_t) + id.size();
   AppendU64(bytes, payload_offset);
   AppendU64(bytes, payload.size());
-  AppendU64(bytes, BundleID.size());
-  bytes.insert(bytes.end(), BundleID.bytes_begin(), BundleID.bytes_end());
+  AppendU64(bytes, id.size());
+  bytes.insert(bytes.end(), id.bytes_begin(), id.bytes_end());
   bytes.insert(bytes.end(), payload.begin(), payload.end());
   return bytes;
 }
 
-llvm::Expected<TestFile> MakeGPUELF() {
-  return TestFile::fromYaml(R"(
+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
@@ -64,7 +104,8 @@ llvm::Expected<TestFile> MakeGPUELF() {
   ABIVersion:      0x1
   Type:            ET_DYN
   Machine:         EM_AMDGPU
-  Flags:           [ EF_AMDGPU_MACH_AMDGCN_GFX942 ]
+  Flags:           [ )" +
+                     elf_flag.str() + R"( ]
 Sections:
   - Name:            .text
     Type:            SHT_PROGBITS
@@ -73,7 +114,8 @@ llvm::Expected<TestFile> MakeGPUELF() {
     AddressAlign:    0x4
     Content:         '00000000'
 ...
-)");
+)";
+  return TestFile::fromYaml(yaml);
 }
 
 llvm::Expected<TestFile>
@@ -94,6 +136,24 @@ MakeELFContainingBundle(llvm::ArrayRef<uint8_t> bundle) {
   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>
@@ -102,17 +162,67 @@ class ObjectContainerClangOffloadBundleTest : public 
::testing::Test {
 
 } // namespace
 
-TEST_F(ObjectContainerClangOffloadBundleTest, LoadsCompressedDeviceImage) {
+TEST_F(ObjectContainerClangOffloadBundleTest, FindsAndLoadsDeviceImage) {
+  constexpr llvm::StringLiteral BundleID = "hipv4-amdgcn-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);
+}
+
+TEST_F(ObjectContainerClangOffloadBundleTest,
+       FindsAndLoadsCompressedDeviceImage) {
   if (!llvm::compression::zlib::isAvailable())
     GTEST_SKIP() << "zlib is unavailable";
 
-  auto gpu_file = MakeGPUELF();
+  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(gpu_bytes);
+  std::vector<uint8_t> bundle =
+      MakeBundle("hipv4-amdgcn-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(
@@ -126,19 +236,20 @@ TEST_F(ObjectContainerClangOffloadBundleTest, 
LoadsCompressedDeviceImage) {
       compressed_bytes.size()));
   ASSERT_THAT_EXPECTED(bundled_file, llvm::Succeeded());
 
-  llvm::Expected<llvm::sys::fs::TempFile> temp_file =
-      bundled_file->writeToTemporaryFile();
+  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 specs = ObjectFile::GetModuleSpecifications(file, 0, 0);
-  ASSERT_EQ(2u, specs.GetSize());
+  ModuleSpecList all_specs =
+      ObjectFile::GetModuleSpecifications(file, /*file_offset=*/0,
+                                          /*file_size=*/0);
+  ASSERT_EQ(all_specs.GetSize(), 2u);
 
   ModuleSpec device_spec;
-  ASSERT_TRUE(specs.GetModuleSpecAtIndex(1, 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);
@@ -149,12 +260,91 @@ TEST_F(ObjectContainerClangOffloadBundleTest, 
LoadsCompressedDeviceImage) {
   EXPECT_EQ(module_sp->GetObjectFile()->GetArchitecture().GetClangTargetCPU(),
             "gfx942");
 
-  ModuleSpec requested(file);
-  requested.GetArchitecture() = device_spec.GetArchitecture();
-  auto selected_module_sp = std::make_shared<Module>(requested);
-  ASSERT_NE(selected_module_sp->GetObjectFile(), nullptr);
-  EXPECT_EQ(selected_module_sp->GetObjectFile()
+  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-amdgcn-amd-amdhsa--gfx908",
+       {gfx908_data->GetDataStart(), gfx908_data->GetByteSize()}},
+      {"hipv4-amdgcn-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