https://github.com/satyajanga updated https://github.com/llvm/llvm-project/pull/222362
>From 3b7e4d29772c0dc01260cdd44264c05b682213ee 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 | 198 ++++++++++++++++++ .../ObjectContainerClangOffloadBundle.h | 75 +++++++ lldb/source/Symbol/ObjectFile.cpp | 21 +- 5 files changed, 301 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..908a3a6250ed0 --- /dev/null +++ b/lldb/source/Plugins/ObjectContainer/Clang-Offload-Bundle/ObjectContainerClangOffloadBundle.cpp @@ -0,0 +1,198 @@ +//===-- 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(); + 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 b3bad22ed289a4d690677151107cee9659e413f5 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 908a3a6250ed0..02625ee2fdba2 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; @@ -76,35 +79,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 020f0c40c11f8a3ac7f4ccb9e06dad0cca38bfa0 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 02625ee2fdba2..965ac60f6c320 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" @@ -121,10 +122,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)); } @@ -197,7 +219,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); @@ -223,7 +249,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 e1c25d9b53682b6585be65bcc33355491dce0a93 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..182a1c1214bb3 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-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); +} + +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-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( @@ -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-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
