[clang] clang-offload-bundler incorrectly errors on multi-CCOB binaries (PR #182579)
David Salinas via cfe-commits
cfe-commits at lists.llvm.org
Fri Apr 17 08:23:37 PDT 2026
https://github.com/david-salinas updated https://github.com/llvm/llvm-project/pull/182579
>From 2ed86ba4294fbf589cc603d62f7ea24b2ee33fd1 Mon Sep 17 00:00:00 2001
From: dsalinas_amdeng <david.salinas at amd.com>
Date: Thu, 19 Feb 2026 23:44:28 +0000
Subject: [PATCH 1/7] clang-offload-bundler incorrectly errors on multi-CCOB
binaries
---
clang/lib/Driver/OffloadBundler.cpp | 156 ++++++++++++++++++----------
1 file changed, 102 insertions(+), 54 deletions(-)
diff --git a/clang/lib/Driver/OffloadBundler.cpp b/clang/lib/Driver/OffloadBundler.cpp
index 8dd57c5c3b4a5..61b2b68aa4e4b 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -192,13 +192,13 @@ class FileHandler {
/// Update the file handler with information from the header of the bundled
/// file.
- virtual Error ReadHeader(MemoryBuffer &Input) = 0;
+ virtual Error ReadHeader(StringRef FC) = 0;
/// Read the marker of the next bundled to be read in the file. The bundle
/// name is returned if there is one in the file, or `std::nullopt` if there
/// are no more bundles to be read.
virtual Expected<std::optional<StringRef>>
- ReadBundleStart(MemoryBuffer &Input) = 0;
+ ReadBundleStart(StringRef Input) = 0;
/// Read the marker that closes the current bundle.
virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
@@ -227,33 +227,52 @@ class FileHandler {
/// List bundle IDs in \a Input.
virtual Error listBundleIDs(MemoryBuffer &Input) {
- if (Error Err = ReadHeader(Input))
- return Err;
- return forEachBundle(Input, [&](const BundleInfo &Info) -> Error {
- llvm::outs() << Info.BundleID << '\n';
- Error Err = listBundleIDsCallback(Input, Info);
+ size_t NextbundleStart = 0;
+ StringRef BufferString = Input.getBuffer();
+ while ((NextbundleStart != StringRef::npos)) {
+ BufferString = BufferString.drop_front(NextbundleStart);
+
+ // Read the header.
+ Error Err = ReadHeader(BufferString);
if (Err)
return Err;
- return Error::success();
- });
+
+ Err = forEachBundle(BufferString, [&](const BundleInfo &Info) -> Error {
+ llvm::outs() << Info.BundleID << '\n';
+ Error Err = listBundleIDsCallback(Input, Info);
+ if (Err)
+ return Err;
+ return Error::success();
+ });
+
+ if (Err)
+ return Err;
+
+ // Find the beginning of the next Bundle, if it exists.
+ NextbundleStart = BufferString.find(StringRef(OFFLOAD_BUNDLER_MAGIC_STR),
+ sizeof(OFFLOAD_BUNDLER_MAGIC_STR));
+ }
+ return Error::success();
}
/// Get bundle IDs in \a Input in \a BundleIds.
virtual Error getBundleIDs(MemoryBuffer &Input,
std::set<StringRef> &BundleIds) {
- if (Error Err = ReadHeader(Input))
+
+ if (Error Err = ReadHeader(Input.getBuffer()))
return Err;
- return forEachBundle(Input, [&](const BundleInfo &Info) -> Error {
- BundleIds.insert(Info.BundleID);
- Error Err = listBundleIDsCallback(Input, Info);
- if (Err)
- return Err;
- return Error::success();
- });
+ return forEachBundle(Input.getBuffer(),
+ [&](const BundleInfo &Info) -> Error {
+ BundleIds.insert(Info.BundleID);
+ Error Err = listBundleIDsCallback(Input, Info);
+ if (Err)
+ return Err;
+ return Error::success();
+ });
}
/// For each bundle in \a Input, do \a Func.
- Error forEachBundle(MemoryBuffer &Input,
+ Error forEachBundle(StringRef Input,
std::function<Error(const BundleInfo &)> Func) {
while (true) {
Expected<std::optional<StringRef>> CurTripleOrErr =
@@ -347,9 +366,7 @@ class BinaryFileHandler final : public FileHandler {
~BinaryFileHandler() final {}
- Error ReadHeader(MemoryBuffer &Input) final {
- StringRef FC = Input.getBuffer();
-
+ Error ReadHeader(StringRef FC) final {
// Initialize the current bundle with the end of the container.
CurBundleInfo = BundlesInfo.end();
@@ -404,7 +421,6 @@ class BinaryFileHandler final : public FileHandler {
if (!Offset || Offset + Size > FC.size())
return Error::success();
- assert(!BundlesInfo.contains(Triple) && "Triple is duplicated??");
BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset);
}
// Set the iterator to where we will start to read.
@@ -413,8 +429,7 @@ class BinaryFileHandler final : public FileHandler {
return Error::success();
}
- Expected<std::optional<StringRef>>
- ReadBundleStart(MemoryBuffer &Input) final {
+ Expected<std::optional<StringRef>> ReadBundleStart(StringRef Input) final {
if (NextBundleInfo == BundlesInfo.end())
return std::nullopt;
CurBundleInfo = NextBundleInfo++;
@@ -578,10 +593,9 @@ class ObjectFileHandler final : public FileHandler {
~ObjectFileHandler() final {}
- Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
+ Error ReadHeader(StringRef Input) final { return Error::success(); }
- Expected<std::optional<StringRef>>
- ReadBundleStart(MemoryBuffer &Input) final {
+ Expected<std::optional<StringRef>> ReadBundleStart(StringRef Input) final {
while (NextSection != Obj->section_end()) {
CurrentSection = NextSection;
++NextSection;
@@ -789,11 +803,9 @@ class TextFileHandler final : public FileHandler {
size_t ReadChars = 0u;
protected:
- Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
+ Error ReadHeader(StringRef Input) final { return Error::success(); }
- Expected<std::optional<StringRef>>
- ReadBundleStart(MemoryBuffer &Input) final {
- StringRef FC = Input.getBuffer();
+ Expected<std::optional<StringRef>> ReadBundleStart(StringRef FC) final {
// Find start of the bundle.
ReadChars = FC.find(BundleStartString, ReadChars);
@@ -1267,7 +1279,8 @@ CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input,
DecompressTimer.startTimer();
SmallVector<uint8_t, 0> DecompressedData;
- StringRef CompressedData = Blob.substr(HeaderSize);
+ StringRef CompressedData =
+ Blob.substr(HeaderSize, TotalFileSize - HeaderSize);
if (llvm::Error DecompressionError = llvm::compression::decompress(
CompressionFormat, llvm::arrayRefFromStringRef(CompressedData),
DecompressedData, UncompressedSize))
@@ -1319,6 +1332,17 @@ CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input,
<< "Decompression speed: "
<< llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
<< "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
+ << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
+ << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
+ << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
+ << "Compression rate: "
+ << llvm::format("%.2lf", CompressionRate) << "\n"
+ << "Compression ratio: "
+ << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
+ << "Decompression speed: "
+ << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
+ << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
+ << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
<< "Recalculated hash: "
<< llvm::format_hex(RecalculatedHash, 16) << "\n"
<< "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n";
@@ -1331,32 +1355,56 @@ CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input,
// List bundle IDs. Return true if an error was found.
Error OffloadBundler::ListBundleIDsInFile(
StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) {
+
+ size_t Offset = 0;
+ size_t NextbundleStart = 0;
+ std::unique_ptr<MemoryBuffer> Buffer;
+
// Open Input file.
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
MemoryBuffer::getFileOrSTDIN(InputFileName, /*IsText=*/true);
if (std::error_code EC = CodeOrErr.getError())
return createFileError(InputFileName, EC);
- // Decompress the input if necessary.
- Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
- CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose);
- if (!DecompressedBufferOrErr)
- return createStringError(
- inconvertibleErrorCode(),
- "Failed to decompress input: " +
- llvm::toString(DecompressedBufferOrErr.takeError()));
+ StringRef Buf = (**CodeOrErr).getBuffer();
- MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
+ // There may be multiple bundles.
+ while ((NextbundleStart != StringRef::npos) && (Offset < Buf.size())) {
+ Buf = Buf.drop_front(Offset);
- // Select the right files handler.
- Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
- CreateFileHandler(DecompressedInput, BundlerConfig);
- if (!FileHandlerOrErr)
- return FileHandlerOrErr.takeError();
+ if (identify_magic(Buf) == file_magic::offload_bundle_compressed)
+ // Decompress this bundle first.
+ NextbundleStart = Buf.find("CCOB", sizeof("CCOB"));
+ if (NextbundleStart == StringRef::npos)
+ NextbundleStart = Buf.size();
- std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
- assert(FH);
- return FH->listBundleIDs(DecompressedInput);
+ // Decompress the input if necessary.
+ Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
+ CompressedOffloadBundle::decompress(**CodeOrErr, BundlerConfig.Verbose);
+ if (!DecompressedBufferOrErr)
+ return createStringError(
+ inconvertibleErrorCode(),
+ "Failed to decompress input: " +
+ llvm::toString(DecompressedBufferOrErr.takeError()));
+
+ MemoryBuffer &DecompressedInput = **DecompressedBufferOrErr;
+
+ // Select the right files handler.
+ Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
+ CreateFileHandler(DecompressedInput, BundlerConfig);
+ if (!FileHandlerOrErr)
+ return FileHandlerOrErr.takeError();
+
+ std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
+ assert(FH);
+ Error E = FH->listBundleIDs(DecompressedInput);
+ if (E)
+ return E;
+
+ if (NextbundleStart != StringRef::npos)
+ Offset += NextbundleStart;
+ }
+ return Error::success();
}
/// @brief Checks if a code object \p CodeObjectInfo is compatible with a given
@@ -1560,7 +1608,7 @@ Error OffloadBundler::UnbundleFiles() {
assert(FH);
// Read the header of the bundled file.
- if (Error Err = FH->ReadHeader(Input))
+ if (Error Err = FH->ReadHeader(Input.getBuffer()))
return Err;
// Create a work list that consist of the map triple/output file.
@@ -1579,7 +1627,7 @@ Error OffloadBundler::UnbundleFiles() {
bool FoundHostBundle = false;
while (!Worklist.empty()) {
Expected<std::optional<StringRef>> CurTripleOrErr =
- FH->ReadBundleStart(Input);
+ FH->ReadBundleStart(Input.getBuffer());
if (!CurTripleOrErr)
return CurTripleOrErr.takeError();
@@ -1863,11 +1911,11 @@ Error OffloadBundler::UnbundleArchive() {
assert(FileHandler &&
"FileHandle creation failed for file in the archive!");
- if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer))
+ if (Error ReadErr = FileHandler->ReadHeader(CodeObjectBuffer.getBuffer()))
return ReadErr;
Expected<std::optional<StringRef>> CurBundleIDOrErr =
- FileHandler->ReadBundleStart(CodeObjectBuffer);
+ FileHandler->ReadBundleStart(CodeObjectBuffer.getBuffer());
if (!CurBundleIDOrErr)
return CurBundleIDOrErr.takeError();
@@ -1923,7 +1971,7 @@ Error OffloadBundler::UnbundleArchive() {
return Err;
Expected<std::optional<StringRef>> NextTripleOrErr =
- FileHandler->ReadBundleStart(CodeObjectBuffer);
+ FileHandler->ReadBundleStart(CodeObjectBuffer.getBuffer());
if (!NextTripleOrErr)
return NextTripleOrErr.takeError();
>From 46e333837aca6ed77ddfd59ed275ab68b3dad2c0 Mon Sep 17 00:00:00 2001
From: david-salinas <dsalinas at amd.com>
Date: Fri, 27 Feb 2026 21:08:35 +0000
Subject: [PATCH 2/7] Fix clang-offload-bundler handling of compressed offload
bundles
---
clang/lib/Driver/OffloadBundler.cpp | 44 +++++++++++++++++++++--------
1 file changed, 32 insertions(+), 12 deletions(-)
diff --git a/clang/lib/Driver/OffloadBundler.cpp b/clang/lib/Driver/OffloadBundler.cpp
index 61b2b68aa4e4b..67422aaaa6456 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -228,8 +228,12 @@ class FileHandler {
/// List bundle IDs in \a Input.
virtual Error listBundleIDs(MemoryBuffer &Input) {
size_t NextbundleStart = 0;
+ size_t Offset = 0;
StringRef BufferString = Input.getBuffer();
- while ((NextbundleStart != StringRef::npos)) {
+ while ((NextbundleStart != StringRef::npos) &&
+ (Offset < BufferString.size())) {
+
+ // Drop the data that has already been processed/read.
BufferString = BufferString.drop_front(NextbundleStart);
// Read the header.
@@ -251,6 +255,9 @@ class FileHandler {
// Find the beginning of the next Bundle, if it exists.
NextbundleStart = BufferString.find(StringRef(OFFLOAD_BUNDLER_MAGIC_STR),
sizeof(OFFLOAD_BUNDLER_MAGIC_STR));
+
+ if (NextbundleStart != StringRef::npos)
+ Offset += NextbundleStart;
}
return Error::success();
}
@@ -1358,25 +1365,39 @@ Error OffloadBundler::ListBundleIDsInFile(
size_t Offset = 0;
size_t NextbundleStart = 0;
+ StringRef Magic; // = OFFLOAD_BUNDLER_MAGIC_STR;
std::unique_ptr<MemoryBuffer> Buffer;
// Open Input file.
- ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
+ // ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
+ ErrorOr<std::unique_ptr<MemoryBuffer>> Contents =
MemoryBuffer::getFileOrSTDIN(InputFileName, /*IsText=*/true);
- if (std::error_code EC = CodeOrErr.getError())
+ if (std::error_code EC = Contents.getError())
return createFileError(InputFileName, EC);
- StringRef Buf = (**CodeOrErr).getBuffer();
+ StringRef Buf = (**Contents).getBuffer();
// There may be multiple bundles.
- while ((NextbundleStart != StringRef::npos) && (Offset < Buf.size())) {
- Buf = Buf.drop_front(Offset);
+ while ((NextbundleStart != StringRef::npos) &&
+ (Offset < (**Contents).getBufferSize())) {
+ Buffer = MemoryBuffer::getMemBuffer(
+ (**Contents).getBuffer().drop_front(Offset), "",
+ /*RequiresNullTerminator=*/false);
+
+ if (identify_magic((*Buffer).getBuffer()) ==
+ file_magic::offload_bundle_compressed) {
+ Magic = "CCOB";
+ NextbundleStart = (*Buffer).getBuffer().find(Magic, Magic.size());
+ } else
+ NextbundleStart = StringRef::npos;
- if (identify_magic(Buf) == file_magic::offload_bundle_compressed)
- // Decompress this bundle first.
- NextbundleStart = Buf.find("CCOB", sizeof("CCOB"));
- if (NextbundleStart == StringRef::npos)
- NextbundleStart = Buf.size();
+ ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
+ MemoryBuffer::getMemBuffer(
+ (*Buffer).getBuffer().take_front(NextbundleStart),
+ InputFileName, // FileName,
+ false);
+ if (std::error_code EC = CodeOrErr.getError())
+ return createFileError(InputFileName, EC);
// Decompress the input if necessary.
Expected<std::unique_ptr<MemoryBuffer>> DecompressedBufferOrErr =
@@ -1394,7 +1415,6 @@ Error OffloadBundler::ListBundleIDsInFile(
CreateFileHandler(DecompressedInput, BundlerConfig);
if (!FileHandlerOrErr)
return FileHandlerOrErr.takeError();
-
std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
assert(FH);
Error E = FH->listBundleIDs(DecompressedInput);
>From 57b6393c8cde2c5175f59821dc459bfcb595f461 Mon Sep 17 00:00:00 2001
From: david-salinas <dsalinas at amd.com>
Date: Thu, 5 Mar 2026 18:32:01 +0000
Subject: [PATCH 3/7] Clean Up formatting and build errors
---
clang/lib/Driver/OffloadBundler.cpp | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/clang/lib/Driver/OffloadBundler.cpp b/clang/lib/Driver/OffloadBundler.cpp
index 67422aaaa6456..bb726fdd537c9 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -257,7 +257,7 @@ class FileHandler {
sizeof(OFFLOAD_BUNDLER_MAGIC_STR));
if (NextbundleStart != StringRef::npos)
- Offset += NextbundleStart;
+ Offset += NextbundleStart;
}
return Error::success();
}
@@ -1375,8 +1375,6 @@ Error OffloadBundler::ListBundleIDsInFile(
if (std::error_code EC = Contents.getError())
return createFileError(InputFileName, EC);
- StringRef Buf = (**Contents).getBuffer();
-
// There may be multiple bundles.
while ((NextbundleStart != StringRef::npos) &&
(Offset < (**Contents).getBufferSize())) {
>From 79aaf1d272cef2cfb0569bd56d5e1b64b398704d Mon Sep 17 00:00:00 2001
From: david-salinas <dsalinas at amd.com>
Date: Fri, 27 Mar 2026 16:26:18 +0000
Subject: [PATCH 4/7] clean up.
---
clang/lib/Driver/OffloadBundler.cpp | 11 -----------
1 file changed, 11 deletions(-)
diff --git a/clang/lib/Driver/OffloadBundler.cpp b/clang/lib/Driver/OffloadBundler.cpp
index bb726fdd537c9..4d409d709a61c 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -1339,17 +1339,6 @@ CompressedOffloadBundle::decompress(const llvm::MemoryBuffer &Input,
<< "Decompression speed: "
<< llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
<< "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
- << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
- << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
- << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
- << "Compression rate: "
- << llvm::format("%.2lf", CompressionRate) << "\n"
- << "Compression ratio: "
- << llvm::format("%.2lf%%", 100.0 / CompressionRate) << "\n"
- << "Decompression speed: "
- << llvm::format("%.2lf MB/s", DecompressionSpeedMBs) << "\n"
- << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
- << "Stored hash: " << llvm::format_hex(StoredHash, 16) << "\n"
<< "Recalculated hash: "
<< llvm::format_hex(RecalculatedHash, 16) << "\n"
<< "Hashes match: " << (HashMatch ? "Yes" : "No") << "\n";
>From 86d0f7babc81a063bcb5e74ecb609ac91860a564 Mon Sep 17 00:00:00 2001
From: david-salinas <dsalinas at amd.com>
Date: Wed, 1 Apr 2026 19:00:17 +0000
Subject: [PATCH 5/7] Address PR comments.
---
clang/lib/Driver/OffloadBundler.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang/lib/Driver/OffloadBundler.cpp b/clang/lib/Driver/OffloadBundler.cpp
index 4d409d709a61c..7e34e5fde6f4b 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -1358,7 +1358,6 @@ Error OffloadBundler::ListBundleIDsInFile(
std::unique_ptr<MemoryBuffer> Buffer;
// Open Input file.
- // ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
ErrorOr<std::unique_ptr<MemoryBuffer>> Contents =
MemoryBuffer::getFileOrSTDIN(InputFileName, /*IsText=*/true);
if (std::error_code EC = Contents.getError())
>From 000390203c87508e7d1bbf2002bb9ce819cef1b8 Mon Sep 17 00:00:00 2001
From: david-salinas <dsalinas at amd.com>
Date: Wed, 8 Apr 2026 19:58:35 +0000
Subject: [PATCH 6/7] Adress PR comments.
---
clang/lib/Driver/OffloadBundler.cpp | 28 +++++++++++++---------------
1 file changed, 13 insertions(+), 15 deletions(-)
diff --git a/clang/lib/Driver/OffloadBundler.cpp b/clang/lib/Driver/OffloadBundler.cpp
index 7e34e5fde6f4b..420639b2fe1f5 100644
--- a/clang/lib/Driver/OffloadBundler.cpp
+++ b/clang/lib/Driver/OffloadBundler.cpp
@@ -227,14 +227,14 @@ class FileHandler {
/// List bundle IDs in \a Input.
virtual Error listBundleIDs(MemoryBuffer &Input) {
- size_t NextbundleStart = 0;
+ size_t NextBundleStart = 0;
size_t Offset = 0;
StringRef BufferString = Input.getBuffer();
- while ((NextbundleStart != StringRef::npos) &&
+ while ((NextBundleStart != StringRef::npos) &&
(Offset < BufferString.size())) {
// Drop the data that has already been processed/read.
- BufferString = BufferString.drop_front(NextbundleStart);
+ BufferString = BufferString.drop_front(NextBundleStart);
// Read the header.
Error Err = ReadHeader(BufferString);
@@ -253,11 +253,11 @@ class FileHandler {
return Err;
// Find the beginning of the next Bundle, if it exists.
- NextbundleStart = BufferString.find(StringRef(OFFLOAD_BUNDLER_MAGIC_STR),
+ NextBundleStart = BufferString.find(StringRef(OFFLOAD_BUNDLER_MAGIC_STR),
sizeof(OFFLOAD_BUNDLER_MAGIC_STR));
- if (NextbundleStart != StringRef::npos)
- Offset += NextbundleStart;
+ if (NextBundleStart != StringRef::npos)
+ Offset += NextBundleStart;
}
return Error::success();
}
@@ -1353,8 +1353,7 @@ Error OffloadBundler::ListBundleIDsInFile(
StringRef InputFileName, const OffloadBundlerConfig &BundlerConfig) {
size_t Offset = 0;
- size_t NextbundleStart = 0;
- StringRef Magic; // = OFFLOAD_BUNDLER_MAGIC_STR;
+ size_t NextBundleStart = 0;
std::unique_ptr<MemoryBuffer> Buffer;
// Open Input file.
@@ -1364,7 +1363,7 @@ Error OffloadBundler::ListBundleIDsInFile(
return createFileError(InputFileName, EC);
// There may be multiple bundles.
- while ((NextbundleStart != StringRef::npos) &&
+ while ((NextBundleStart != StringRef::npos) &&
(Offset < (**Contents).getBufferSize())) {
Buffer = MemoryBuffer::getMemBuffer(
(**Contents).getBuffer().drop_front(Offset), "",
@@ -1372,14 +1371,13 @@ Error OffloadBundler::ListBundleIDsInFile(
if (identify_magic((*Buffer).getBuffer()) ==
file_magic::offload_bundle_compressed) {
- Magic = "CCOB";
- NextbundleStart = (*Buffer).getBuffer().find(Magic, Magic.size());
+ NextBundleStart = (*Buffer).getBuffer().find("CCOB", 4);
} else
- NextbundleStart = StringRef::npos;
+ NextBundleStart = StringRef::npos;
ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
MemoryBuffer::getMemBuffer(
- (*Buffer).getBuffer().take_front(NextbundleStart),
+ (*Buffer).getBuffer().take_front(NextBundleStart),
InputFileName, // FileName,
false);
if (std::error_code EC = CodeOrErr.getError())
@@ -1407,8 +1405,8 @@ Error OffloadBundler::ListBundleIDsInFile(
if (E)
return E;
- if (NextbundleStart != StringRef::npos)
- Offset += NextbundleStart;
+ if (NextBundleStart != StringRef::npos)
+ Offset += NextBundleStart;
}
return Error::success();
}
>From 40b72feac0b783c598b838270665d509570a0ee1 Mon Sep 17 00:00:00 2001
From: david-salinas <dsalinas at amd.com>
Date: Fri, 17 Apr 2026 15:14:31 +0000
Subject: [PATCH 7/7] Add a clang-offload-bundler test
---
.../clang-offload-bundler-multi-compress.c | 77 +++++++++++++++++++
1 file changed, 77 insertions(+)
create mode 100644 clang/test/Driver/clang-offload-bundler-multi-compress.c
diff --git a/clang/test/Driver/clang-offload-bundler-multi-compress.c b/clang/test/Driver/clang-offload-bundler-multi-compress.c
new file mode 100644
index 0000000000000..4cfdd0e2fce8a
--- /dev/null
+++ b/clang/test/Driver/clang-offload-bundler-multi-compress.c
@@ -0,0 +1,77 @@
+// REQUIRES: x86-registered-target
+// REQUIRES: zlib || zstd
+// UNSUPPORTED: target={{.*}}-darwin{{.*}}, target={{.*}}-aix{{.*}}, target={{.*}}-zos{{.*}}
+
+// Tests that clang-offload-bundler --list correctly enumerates all bundle IDs
+// from multiple concatenated compressed (CCOB) fat binary blobs stored in the
+// .hip_fatbin section of an ELF object. This models the layout produced when
+// a shared library or relocatable object is linked from multiple HIP
+// translation units, each of which contributes its own CCOB blob to the
+// .hip_fatbin section.
+
+//
+// Create device content files for two simulated translation units.
+//
+// RUN: echo 'Content of device file 1' > %t.dev1
+// RUN: echo 'Content of device file 2' > %t.dev2
+
+//
+// Produce two compressed fat binary blobs with distinct GPU targets so that
+// the FileCheck assertions below are unambiguous.
+//
+// Bundle 1: gfx906 + gfx908
+// RUN: clang-offload-bundler -compress -type=bc \
+// RUN: -targets=hip-amdgcn-amd-amdhsa--gfx906,hip-amdgcn-amd-amdhsa--gfx908 \
+// RUN: -input=%t.dev1 -input=%t.dev2 \
+// RUN: -output=%t.bundle1.ccob
+
+// Bundle 2: gfx1030 + gfx1100
+// RUN: clang-offload-bundler -compress -type=bc \
+// RUN: -targets=hip-amdgcn-amd-amdhsa--gfx1030,hip-amdgcn-amd-amdhsa--gfx1100 \
+// RUN: -input=%t.dev1 -input=%t.dev2 \
+// RUN: -output=%t.bundle2.ccob
+
+//
+// Baseline: --list on each individual compressed bundle must work.
+//
+// RUN: clang-offload-bundler -type=bc -list -input=%t.bundle1.ccob \
+// RUN: | FileCheck %s --check-prefix=SINGLE1
+// SINGLE1-DAG: hip-amdgcn-amd-amdhsa--gfx906
+// SINGLE1-DAG: hip-amdgcn-amd-amdhsa--gfx908
+
+// RUN: clang-offload-bundler -type=bc -list -input=%t.bundle2.ccob \
+// RUN: | FileCheck %s --check-prefix=SINGLE2
+// SINGLE2-DAG: hip-amdgcn-amd-amdhsa--gfx1030
+// SINGLE2-DAG: hip-amdgcn-amd-amdhsa--gfx1100
+
+//
+// Concatenate the two CCOB blobs. This mirrors what the linker does when it
+// merges the .hip_fatbin input sections contributed by multiple TUs.
+//
+// RUN: cat %t.bundle1.ccob %t.bundle2.ccob > %t.multi.fatbin
+
+//
+// Build a host object and inject the concatenated blob as its .hip_fatbin
+// section, replicating the ELF layout of a fat shared library.
+//
+// RUN: %clang -O0 -target %itanium_abi_triple %s -c -o %t.host.o
+// RUN: llvm-objcopy \
+// RUN: --add-section=.hip_fatbin=%t.multi.fatbin \
+// RUN: --set-section-flags=.hip_fatbin=alloc \
+// RUN: %t.host.o %t.multi.o
+
+//
+// --list on an object whose .hip_fatbin section contains two concatenated
+// CCOB blobs must enumerate all bundle IDs from both fat binaries.
+//
+// RUN: clang-offload-bundler -type=o -list -input=%t.multi.fatbin \
+// RUN: | FileCheck %s --check-prefix=MULTI
+// MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx906
+// MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx908
+// MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx1030
+// MULTI-DAG: hip-amdgcn-amd-amdhsa--gfx1100
+
+// Some code so that we can compile this file as a host object.
+int A = 0;
+void test_func(void) { ++A; }
+
More information about the cfe-commits
mailing list