Author: Yury Plyakhin
Date: 2026-02-05T07:46:57-08:00
New Revision: 4d27530c69fdd692a69dc905f50b076d336da11a

URL: 
https://github.com/llvm/llvm-project/commit/4d27530c69fdd692a69dc905f50b076d336da11a
DIFF: 
https://github.com/llvm/llvm-project/commit/4d27530c69fdd692a69dc905f50b076d336da11a.diff

LOG: [Offloading] Offload Binary Format V2: Support Multiple Entries (#169425)

This PR updates the OffloadBinary format from version 1 to version 2,
enabling support for multiple offloading entries in a single binary.
This allows combining multiple device images into a single binary with
common global metadata while maintaining backwards compatibility with
version 1 binaries.

# Key Changes
## Binary Format Enhancements
  **Version 2 Format Changes:**
  - Changed from single-entry to multi-entry design
  - Updated `Header` structure:
    - Renamed `EntryOffset` → `EntriesOffset` (offset to entries array)
    - Renamed `EntrySize` → `EntriesCount` (number of entries)
- Added `StringEntry::ValueSize` field to support explicit string value
sizes (enables non-null-terminated strings)
- Introduced `OffloadEntryFlags` enum with `OIF_Metadata` flag for
metadata-only entries (entries without binary images)

  **API Changes:**
- `OffloadBinary::create()` now returns
`Expected<SmallVector<std::unique_ptr<OffloadBinary>>>` instead of
single binary
- Added optional `Index` parameter to extract specific entry:
`create(Buffer, std::optional<uint64_t> Index)`
- `OffloadBinary::write()` now accepts `ArrayRef<OffloadingImage>`
instead of single image
  - Added `OffloadBinary::extractHeader()` for header extraction

  **Memory Management:**
- Implemented `SharedMemoryBuffer` class to enable memory sharing across
multiple `OffloadBinary` instances from the same file
- Multiple entries from a single serialized binary share the underlying
buffer

## Testing
  **Unit Tests (`unittests/Object/OffloadingTest.cpp`):**
- `checkMultiEntryBinaryExtraction`: Tests extracting all entries from a
multi-entry binary
- `checkIndexBasedExtraction`: Tests extracting specific entries by
index, including out-of-bounds validation
  - `checkEdgeCases`: Tests edge cases including:
    - Empty string metadata
    - Empty image data
    - Large string values (4KB)

  **Other Tests:**
- Updated `test/ObjectYAML/Offload/multiple_members.yaml` to include
metadata-only entry

---------

Co-authored-by: Joseph Huber <[email protected]>

Added: 
    llvm/test/ObjectYAML/Offload/malformed-entries-count.yaml

Modified: 
    clang/test/Driver/linker-wrapper-image.c
    llvm/include/llvm/Object/OffloadBinary.h
    llvm/include/llvm/ObjectYAML/OffloadYAML.h
    llvm/lib/Frontend/Offloading/OffloadWrapper.cpp
    llvm/lib/Object/Binary.cpp
    llvm/lib/Object/OffloadBinary.cpp
    llvm/lib/ObjectYAML/OffloadEmitter.cpp
    llvm/lib/ObjectYAML/OffloadYAML.cpp
    llvm/test/ObjectYAML/Offload/malformed-offset.yaml
    llvm/test/ObjectYAML/Offload/malformed-version.yaml
    llvm/test/ObjectYAML/Offload/multiple_members.yaml
    llvm/tools/obj2yaml/offload2yaml.cpp
    llvm/unittests/Object/OffloadingTest.cpp

Removed: 
    llvm/test/ObjectYAML/Offload/malformed-entry-size.yaml


################################################################################
diff  --git a/clang/test/Driver/linker-wrapper-image.c 
b/clang/test/Driver/linker-wrapper-image.c
index b9327121edcf9..2c0df8c6be925 100644
--- a/clang/test/Driver/linker-wrapper-image.c
+++ b/clang/test/Driver/linker-wrapper-image.c
@@ -25,7 +25,7 @@
 // OPENMP-REL: @.omp_offloading.device_image = internal unnamed_addr constant 
[[[SIZE:[0-9]+]] x i8] c"\10\FF\10\AD{{.*}}", section 
".llvm.offloading.relocatable", align 8
 
 //      OPENMP: @.omp_offloading.device_image = internal unnamed_addr constant 
[[[SIZE:[0-9]+]] x i8] c"\10\FF\10\AD{{.*}}", section ".llvm.offloading", align 
8
-// OPENMP-NEXT: @.omp_offloading.device_images = internal unnamed_addr 
constant [1 x %__tgt_device_image] [%__tgt_device_image { ptr getelementptr 
([[[BEGIN:[0-9]+]] x i8], ptr @.omp_offloading.device_image, i64 0, i64 144), 
ptr getelementptr ([[[END:[0-9]+]] x i8], ptr @.omp_offloading.device_image, 
i64 0, i64 144), ptr @__start_llvm_offload_entries, ptr 
@__stop_llvm_offload_entries }]
+// OPENMP-NEXT: @.omp_offloading.device_images = internal unnamed_addr 
constant [1 x %__tgt_device_image] [%__tgt_device_image { ptr getelementptr 
([[[IMG_OFF:[0-9]+]] x i8], ptr @.omp_offloading.device_image, i64 0, i64 
[[IMG_OFF]]), ptr getelementptr ([[[IMG_OFF]] x i8], ptr 
@.omp_offloading.device_image, i64 0, i64 [[IMG_OFF]]), ptr 
@__start_llvm_offload_entries, ptr @__stop_llvm_offload_entries }]
 // OPENMP-NEXT: @.omp_offloading.descriptor = internal constant 
%__tgt_bin_desc { i32 1, ptr @.omp_offloading.device_images, ptr 
@__start_llvm_offload_entries, ptr @__stop_llvm_offload_entries }
 // OPENMP-NEXT: @llvm.global_ctors = appending global [1 x { i32, ptr, ptr }] 
[{ i32, ptr, ptr } { i32 101, ptr @.omp_offloading.descriptor_reg, ptr null }]
 

diff  --git a/llvm/include/llvm/Object/OffloadBinary.h 
b/llvm/include/llvm/Object/OffloadBinary.h
index f3847c1624977..ff9c7d0d82dc2 100644
--- a/llvm/include/llvm/Object/OffloadBinary.h
+++ b/llvm/include/llvm/Object/OffloadBinary.h
@@ -6,7 +6,7 @@
 //
 
//===----------------------------------------------------------------------===//
 //
-// This file contains the binary format used for budingling device metadata 
with
+// This file contains the binary format used for bundling device metadata with
 // an associated device image. The data can then be stored inside a host object
 // file to create a fat binary and read by the linker. This is intended to be a
 // thin wrapper around the image itself. If this format becomes sufficiently
@@ -52,6 +52,13 @@ enum ImageKind : uint16_t {
   IMG_LAST,
 };
 
+/// Flags associated with the Entry.
+enum OffloadEntryFlags : uint32_t {
+  OIF_None = 0,
+  // Entry doesn't contain an image. Used to keep metadata only entries.
+  OIF_Metadata = (1 << 0),
+};
+
 /// A simple binary serialization of an offloading file. We use this format to
 /// embed the offloading image into the host executable so it can be extracted
 /// and used by the linker.
@@ -67,7 +74,7 @@ class OffloadBinary : public Binary {
   using string_iterator_range = iterator_range<string_iterator>;
 
   /// The current version of the binary used for backwards compatibility.
-  static const uint32_t Version = 1;
+  static const uint32_t Version = 2;
 
   /// The offloading metadata that will be serialized to a memory buffer.
   struct OffloadingImage {
@@ -78,12 +85,57 @@ class OffloadBinary : public Binary {
     std::unique_ptr<MemoryBuffer> Image;
   };
 
-  /// Attempt to parse the offloading binary stored in \p Data.
-  LLVM_ABI static Expected<std::unique_ptr<OffloadBinary>>
-      create(MemoryBufferRef);
+  struct Header {
+    uint8_t Magic[4] = {0x10, 0xFF, 0x10, 0xAD}; // 0x10FF10AD magic bytes.
+    uint32_t Version = OffloadBinary::Version;   // Version identifier.
+    uint64_t Size;          // Size in bytes of this entire binary.
+    uint64_t EntriesOffset; // Offset in bytes to the start of entries block.
+    uint64_t EntriesCount;  // Number of metadata entries in the binary.
+  };
+
+  struct Entry {
+    ImageKind TheImageKind;     // The kind of the image stored.
+    OffloadKind TheOffloadKind; // The producer of this image.
+    uint32_t Flags;             // Additional flags associated with the entry.
+    uint64_t StringOffset;      // Offset in bytes to the string map.
+    uint64_t NumStrings;        // Number of entries in the string map.
+    uint64_t ImageOffset;       // Offset in bytes of the actual binary image.
+    uint64_t ImageSize;         // Size in bytes of the binary image.
+  };
 
-  /// Serialize the contents of \p File to a binary buffer to be read later.
-  LLVM_ABI static SmallString<0> write(const OffloadingImage &);
+  struct StringEntry {
+    uint64_t KeyOffset;
+    uint64_t ValueOffset;
+    uint64_t ValueSize; // Size of the value in bytes.
+  };
+
+  struct StringEntryV1 {
+    uint64_t KeyOffset;
+    uint64_t ValueOffset;
+  };
+
+  /// Attempt to extract and validate the header from the offloading binary in
+  /// \p Buf.
+  LLVM_ABI
+  static Expected<const Header *> extractHeader(MemoryBufferRef Buf);
+
+  /// Attempt to parse the offloading binary stored in \p Buf.
+  /// For version 1 binaries, always returns a single OffloadBinary.
+  /// For version 2+ binaries:
+  ///   - If \p Index is provided, returns the OffloadBinary at that index.
+  ///   - If \p Index is std::nullopt, returns all OffloadBinary entries.
+  /// \param Buf The memory buffer containing the offload binary.
+  /// \param Index Optional index to select a specific entry. If not provided,
+  ///              all entries are returned (version 2+ only).
+  /// \returns An array of unique pointers to OffloadBinary objects, or an
+  /// error.
+  LLVM_ABI static Expected<SmallVector<std::unique_ptr<OffloadBinary>>>
+  create(MemoryBufferRef Buf, std::optional<uint64_t> Index = std::nullopt);
+
+  /// Serialize the contents of \p OffloadingData to a binary buffer to be read
+  /// later.
+  LLVM_ABI static SmallString<0>
+  write(ArrayRef<OffloadingImage> OffloadingData);
 
   static uint64_t getAlignment() { return 8; }
 
@@ -92,6 +144,7 @@ class OffloadBinary : public Binary {
   uint32_t getVersion() const { return TheHeader->Version; }
   uint32_t getFlags() const { return TheEntry->Flags; }
   uint64_t getSize() const { return TheHeader->Size; }
+  uint64_t getIndex() const { return Index; }
 
   StringRef getTriple() const { return getString("triple"); }
   StringRef getArch() const { return getString("arch"); }
@@ -106,39 +159,29 @@ class OffloadBinary : public Binary {
 
   static bool classof(const Binary *V) { return V->isOffloadFile(); }
 
-  struct Header {
-    uint8_t Magic[4] = {0x10, 0xFF, 0x10, 0xAD}; // 0x10FF10AD magic bytes.
-    uint32_t Version = OffloadBinary::Version;   // Version identifier.
-    uint64_t Size;        // Size in bytes of this entire binary.
-    uint64_t EntryOffset; // Offset of the metadata entry in bytes.
-    uint64_t EntrySize;   // Size of the metadata entry in bytes.
-  };
-
-  struct Entry {
-    ImageKind TheImageKind;     // The kind of the image stored.
-    OffloadKind TheOffloadKind; // The producer of this image.
-    uint32_t Flags;             // Additional flags associated with the image.
-    uint64_t StringOffset;      // Offset in bytes to the string map.
-    uint64_t NumStrings;        // Number of entries in the string map.
-    uint64_t ImageOffset;       // Offset in bytes of the actual binary image.
-    uint64_t ImageSize;         // Size in bytes of the binary image.
-  };
-
-  struct StringEntry {
-    uint64_t KeyOffset;
-    uint64_t ValueOffset;
-  };
-
 private:
   OffloadBinary(MemoryBufferRef Source, const Header *TheHeader,
-                const Entry *TheEntry)
+                const Entry *TheEntry, const uint64_t Index = 0)
       : Binary(Binary::ID_Offload, Source), Buffer(Source.getBufferStart()),
-        TheHeader(TheHeader), TheEntry(TheEntry) {
-    const StringEntry *StringMapBegin =
-        reinterpret_cast<const StringEntry *>(&Buffer[TheEntry->StringOffset]);
+        TheHeader(TheHeader), TheEntry(TheEntry), Index(Index) {
+    // StringEntryV1 and StringEntry have ABI compatible Key/ValueOffset 
fields,
+    // but 
diff erent sizes, so we need to manually calculate offset.
+    const char *StringMapBegin = &Buffer[TheEntry->StringOffset];
+    const size_t StringEntrySize =
+        TheHeader->Version == 1 ? sizeof(StringEntryV1) : sizeof(StringEntry);
     for (uint64_t I = 0, E = TheEntry->NumStrings; I != E; ++I) {
-      StringRef Key = &Buffer[StringMapBegin[I].KeyOffset];
-      StringData[Key] = &Buffer[StringMapBegin[I].ValueOffset];
+      const char *StringEntryPtr = StringMapBegin + I * StringEntrySize;
+      const StringEntryV1 *EntryV1 =
+          reinterpret_cast<const StringEntryV1 *>(StringEntryPtr);
+      StringRef Key = &Buffer[EntryV1->KeyOffset];
+      if (TheHeader->Version == 1) {
+        StringData[Key] = &Buffer[EntryV1->ValueOffset];
+      } else {
+        const StringEntry *Entry =
+            reinterpret_cast<const StringEntry *>(StringEntryPtr);
+        StringData[Key] =
+            StringRef(&Buffer[Entry->ValueOffset], Entry->ValueSize);
+      }
     }
   }
 
@@ -152,10 +195,13 @@ class OffloadBinary : public Binary {
   const Header *TheHeader;
   /// Location of the metadata entries within the binary.
   const Entry *TheEntry;
+  /// Index of the entry in the list of entries serialized in the Buffer.
+  const uint64_t Index;
 };
 
-/// A class to contain the binary information for a single OffloadBinary that
-/// owns its memory.
+/// A class to contain the binary information for a single OffloadBinary.
+/// Memory is shared between multiple OffloadBinary instances read from
+/// the single serialized offload binary.
 class OffloadFile : public OwningBinary<OffloadBinary> {
 public:
   using TargetID = std::pair<StringRef, StringRef>;
@@ -171,11 +217,12 @@ class OffloadFile : public OwningBinary<OffloadBinary> {
         getBinary()->getMemoryBufferRef().getBufferIdentifier());
 
     // This parsing should never fail because it has already been parsed.
-    auto NewBinaryOrErr = OffloadBinary::create(*Buffer);
+    auto NewBinaryOrErr =
+        OffloadBinary::create(*Buffer, getBinary()->getIndex());
     assert(NewBinaryOrErr && "Failed to parse a copy of the binary?");
     if (!NewBinaryOrErr)
       llvm::consumeError(NewBinaryOrErr.takeError());
-    return OffloadFile(std::move(*NewBinaryOrErr), std::move(Buffer));
+    return OffloadFile(std::move((*NewBinaryOrErr)[0]), std::move(Buffer));
   }
 
   /// We use the Triple and Architecture pair to group linker inputs together.

diff  --git a/llvm/include/llvm/ObjectYAML/OffloadYAML.h 
b/llvm/include/llvm/ObjectYAML/OffloadYAML.h
index f897b52aa8b0e..63ff561f3fcbf 100644
--- a/llvm/include/llvm/ObjectYAML/OffloadYAML.h
+++ b/llvm/include/llvm/ObjectYAML/OffloadYAML.h
@@ -39,8 +39,8 @@ struct Binary {
 
   std::optional<uint32_t> Version;
   std::optional<uint64_t> Size;
-  std::optional<uint64_t> EntryOffset;
-  std::optional<uint64_t> EntrySize;
+  std::optional<uint64_t> EntriesOffset;
+  std::optional<uint64_t> EntriesCount;
   std::vector<Member> Members;
 };
 

diff  --git a/llvm/lib/Frontend/Offloading/OffloadWrapper.cpp 
b/llvm/lib/Frontend/Offloading/OffloadWrapper.cpp
index 5f101cc6c946b..5e341ada1889e 100644
--- a/llvm/lib/Frontend/Offloading/OffloadWrapper.cpp
+++ b/llvm/lib/Frontend/Offloading/OffloadWrapper.cpp
@@ -161,7 +161,7 @@ GlobalVariable *createBinDesc(Module &M, 
ArrayRef<ArrayRef<char>> Bufs,
               Binary.bytes_begin());
       const auto *Entry =
           reinterpret_cast<const object::OffloadBinary::Entry *>(
-              Binary.bytes_begin() + Header->EntryOffset);
+              Binary.bytes_begin() + Header->EntriesOffset);
       BeginOffset = Entry->ImageOffset;
       EndOffset = Entry->ImageOffset + Entry->ImageSize;
     }

diff  --git a/llvm/lib/Object/Binary.cpp b/llvm/lib/Object/Binary.cpp
index da2a7bb0a19da..30414257fa90b 100644
--- a/llvm/lib/Object/Binary.cpp
+++ b/llvm/lib/Object/Binary.cpp
@@ -93,8 +93,12 @@ Expected<std::unique_ptr<Binary>> 
object::createBinary(MemoryBufferRef Buffer,
   case file_magic::spirv_object:
     // Unrecognized object file format.
     return errorCodeToError(object_error::invalid_file_type);
-  case file_magic::offload_binary:
-    return OffloadBinary::create(Buffer);
+  case file_magic::offload_binary: {
+    auto OffloadBinaryOrErr = OffloadBinary::create(Buffer);
+    if (!OffloadBinaryOrErr)
+      return OffloadBinaryOrErr.takeError();
+    return std::move((*OffloadBinaryOrErr)[0]);
+  }
   case file_magic::minidump:
     return MinidumpFile::create(Buffer);
   case file_magic::tapi_file:

diff  --git a/llvm/lib/Object/OffloadBinary.cpp 
b/llvm/lib/Object/OffloadBinary.cpp
index ef93bf1e9bfa7..011e9921daf08 100644
--- a/llvm/lib/Object/OffloadBinary.cpp
+++ b/llvm/lib/Object/OffloadBinary.cpp
@@ -28,6 +28,26 @@ using namespace llvm::object;
 
 namespace {
 
+/// A MemoryBuffer that shares ownership of the underlying memory.
+/// This allows multiple OffloadBinary instances to share the same buffer.
+class SharedMemoryBuffer : public MemoryBuffer {
+public:
+  SharedMemoryBuffer(std::shared_ptr<MemoryBuffer> Buf)
+      : SharedBuf(std::move(Buf)) {
+    init(SharedBuf->getBufferStart(), SharedBuf->getBufferEnd(),
+         /*RequiresNullTerminator=*/false);
+  }
+
+  BufferKind getBufferKind() const override { return MemoryBuffer_Malloc; }
+
+  StringRef getBufferIdentifier() const override {
+    return SharedBuf->getBufferIdentifier();
+  }
+
+private:
+  const std::shared_ptr<MemoryBuffer> SharedBuf;
+};
+
 /// Attempts to extract all the embedded device images contained inside the
 /// buffer \p Contents. The buffer is expected to contain a valid offloading
 /// binary format.
@@ -35,7 +55,7 @@ Error extractOffloadFiles(MemoryBufferRef Contents,
                           SmallVectorImpl<OffloadFile> &Binaries) {
   uint64_t Offset = 0;
   // There could be multiple offloading binaries stored at this section.
-  while (Offset < Contents.getBuffer().size()) {
+  while (Offset < Contents.getBufferSize()) {
     std::unique_ptr<MemoryBuffer> Buffer =
         MemoryBuffer::getMemBuffer(Contents.getBuffer().drop_front(Offset), "",
                                    /*RequiresNullTerminator*/ false);
@@ -43,21 +63,32 @@ Error extractOffloadFiles(MemoryBufferRef Contents,
                        Buffer->getBufferStart()))
       Buffer = MemoryBuffer::getMemBufferCopy(Buffer->getBuffer(),
                                               Buffer->getBufferIdentifier());
-    auto BinaryOrErr = OffloadBinary::create(*Buffer);
-    if (!BinaryOrErr)
-      return BinaryOrErr.takeError();
-    OffloadBinary &Binary = **BinaryOrErr;
 
-    // Create a new owned binary with a copy of the original memory.
+    auto HeaderOrErr = OffloadBinary::extractHeader(*Buffer);
+    if (!HeaderOrErr)
+      return HeaderOrErr.takeError();
+    const OffloadBinary::Header *Header = *HeaderOrErr;
+
+    // Create a copy of original memory containing only the current binary.
     std::unique_ptr<MemoryBuffer> BufferCopy = MemoryBuffer::getMemBufferCopy(
-        Binary.getData().take_front(Binary.getSize()),
+        Buffer->getBuffer().take_front(Header->Size),
         Contents.getBufferIdentifier());
-    auto NewBinaryOrErr = OffloadBinary::create(*BufferCopy);
-    if (!NewBinaryOrErr)
-      return NewBinaryOrErr.takeError();
-    Binaries.emplace_back(std::move(*NewBinaryOrErr), std::move(BufferCopy));
 
-    Offset += Binary.getSize();
+    auto BinariesOrErr = OffloadBinary::create(*BufferCopy);
+    if (!BinariesOrErr)
+      return BinariesOrErr.takeError();
+
+    // Share ownership among multiple OffloadFiles.
+    std::shared_ptr<MemoryBuffer> SharedBuffer =
+        std::shared_ptr<MemoryBuffer>(std::move(BufferCopy));
+
+    for (auto &Binary : *BinariesOrErr) {
+      std::unique_ptr<SharedMemoryBuffer> SharedBufferPtr =
+          std::make_unique<SharedMemoryBuffer>(SharedBuffer);
+      Binaries.emplace_back(std::move(Binary), std::move(SharedBufferPtr));
+    }
+
+    Offset += Header->Size;
   }
 
   return Error::success();
@@ -167,8 +198,8 @@ Error extractFromArchive(const Archive &Library,
 
 } // namespace
 
-Expected<std::unique_ptr<OffloadBinary>>
-OffloadBinary::create(MemoryBufferRef Buf) {
+Expected<const OffloadBinary::Header *>
+OffloadBinary::extractHeader(MemoryBufferRef Buf) {
   if (Buf.getBufferSize() < sizeof(Header) + sizeof(Entry))
     return errorCodeToError(object_error::parse_failed);
 
@@ -182,83 +213,146 @@ OffloadBinary::create(MemoryBufferRef Buf) {
 
   const char *Start = Buf.getBufferStart();
   const Header *TheHeader = reinterpret_cast<const Header *>(Start);
-  if (TheHeader->Version != OffloadBinary::Version)
+  if (TheHeader->Version == 0 || TheHeader->Version > OffloadBinary::Version)
     return errorCodeToError(object_error::parse_failed);
 
   if (TheHeader->Size > Buf.getBufferSize() ||
       TheHeader->Size < sizeof(Entry) || TheHeader->Size < sizeof(Header))
     return errorCodeToError(object_error::unexpected_eof);
 
-  if (TheHeader->EntryOffset > TheHeader->Size - sizeof(Entry) ||
-      TheHeader->EntrySize > TheHeader->Size - sizeof(Header))
+  uint64_t EntriesCount =
+      (TheHeader->Version == 1) ? 1 : TheHeader->EntriesCount;
+  uint64_t EntriesSize = sizeof(Entry) * EntriesCount;
+  if (TheHeader->EntriesOffset > TheHeader->Size - EntriesSize ||
+      EntriesSize > TheHeader->Size - sizeof(Header))
     return errorCodeToError(object_error::unexpected_eof);
 
-  const Entry *TheEntry =
-      reinterpret_cast<const Entry *>(&Start[TheHeader->EntryOffset]);
+  return TheHeader;
+}
+
+Expected<SmallVector<std::unique_ptr<OffloadBinary>>>
+OffloadBinary::create(MemoryBufferRef Buf, std::optional<uint64_t> Index) {
+  auto HeaderOrErr = OffloadBinary::extractHeader(Buf);
+  if (!HeaderOrErr)
+    return HeaderOrErr.takeError();
+  const Header *TheHeader = *HeaderOrErr;
 
-  if (TheEntry->ImageOffset > Buf.getBufferSize() ||
-      TheEntry->StringOffset > Buf.getBufferSize() ||
-      TheEntry->StringOffset + TheEntry->NumStrings * sizeof(StringEntry) >
-          Buf.getBufferSize())
-    return errorCodeToError(object_error::unexpected_eof);
+  const char *Start = Buf.getBufferStart();
+  const Entry *Entries =
+      reinterpret_cast<const Entry *>(&Start[TheHeader->EntriesOffset]);
+
+  auto validateEntry = [&](const Entry *TheEntry) -> Error {
+    if (TheEntry->ImageOffset > Buf.getBufferSize() ||
+        TheEntry->StringOffset > Buf.getBufferSize() ||
+        TheEntry->StringOffset + TheEntry->NumStrings * sizeof(StringEntry) >
+            Buf.getBufferSize())
+      return errorCodeToError(object_error::unexpected_eof);
+    return Error::success();
+  };
+
+  SmallVector<std::unique_ptr<OffloadBinary>> Binaries;
+  if (TheHeader->Version > 1 && Index.has_value()) {
+    if (*Index >= TheHeader->EntriesCount)
+      return errorCodeToError(object_error::parse_failed);
+    const Entry *TheEntry = &Entries[*Index];
+    if (auto Err = validateEntry(TheEntry))
+      return std::move(Err);
+
+    Binaries.emplace_back(new OffloadBinary(Buf, TheHeader, TheEntry, *Index));
+    return Binaries;
+  }
+
+  uint64_t EntriesCount = TheHeader->Version == 1 ? 1 : 
TheHeader->EntriesCount;
+  for (uint64_t I = 0; I < EntriesCount; ++I) {
+    const Entry *TheEntry = &Entries[I];
+    if (auto Err = validateEntry(TheEntry))
+      return std::move(Err);
+
+    Binaries.emplace_back(new OffloadBinary(Buf, TheHeader, TheEntry, I));
+  }
 
-  return std::unique_ptr<OffloadBinary>(
-      new OffloadBinary(Buf, TheHeader, TheEntry));
+  return Binaries;
 }
 
-SmallString<0> OffloadBinary::write(const OffloadingImage &OffloadingData) {
+SmallString<0> OffloadBinary::write(ArrayRef<OffloadingImage> OffloadingData) {
+  uint64_t EntriesCount = OffloadingData.size();
+  assert(EntriesCount > 0 && "At least one offloading image is required");
+
   // Create a null-terminated string table with all the used strings.
+  // Also calculate total size of images.
   StringTableBuilder StrTab(StringTableBuilder::ELF);
-  for (auto &KeyAndValue : OffloadingData.StringData) {
-    StrTab.add(KeyAndValue.first);
-    StrTab.add(KeyAndValue.second);
+  uint64_t TotalStringEntries = 0;
+  uint64_t TotalImagesSize = 0;
+  for (const OffloadingImage &Img : OffloadingData) {
+    for (auto &KeyAndValue : Img.StringData) {
+      StrTab.add(KeyAndValue.first);
+      StrTab.add(KeyAndValue.second);
+    }
+    TotalStringEntries += Img.StringData.size();
+    TotalImagesSize += Img.Image->getBufferSize();
   }
   StrTab.finalize();
 
-  uint64_t StringEntrySize =
-      sizeof(StringEntry) * OffloadingData.StringData.size();
+  uint64_t StringEntrySize = sizeof(StringEntry) * TotalStringEntries;
+  uint64_t EntriesSize = sizeof(Entry) * EntriesCount;
+  uint64_t StrTabOffset = sizeof(Header) + EntriesSize + StringEntrySize;
 
   // Make sure the image we're wrapping around is aligned as well.
-  uint64_t BinaryDataSize = alignTo(sizeof(Header) + sizeof(Entry) +
-                                        StringEntrySize + StrTab.getSize(),
-                                    getAlignment());
+  uint64_t BinaryDataSize =
+      alignTo(StrTabOffset + StrTab.getSize(), getAlignment());
 
-  // Create the header and fill in the offsets. The entry will be directly
+  // Create the header and fill in the offsets. The entries will be directly
   // placed after the header in memory. Align the size to the alignment of the
   // header so this can be placed contiguously in a single section.
   Header TheHeader;
-  TheHeader.Size = alignTo(
-      BinaryDataSize + OffloadingData.Image->getBufferSize(), getAlignment());
-  TheHeader.EntryOffset = sizeof(Header);
-  TheHeader.EntrySize = sizeof(Entry);
-
-  // Create the entry using the string table offsets. The string table will be
-  // placed directly after the entry in memory, and the image after that.
-  Entry TheEntry;
-  TheEntry.TheImageKind = OffloadingData.TheImageKind;
-  TheEntry.TheOffloadKind = OffloadingData.TheOffloadKind;
-  TheEntry.Flags = OffloadingData.Flags;
-  TheEntry.StringOffset = sizeof(Header) + sizeof(Entry);
-  TheEntry.NumStrings = OffloadingData.StringData.size();
-
-  TheEntry.ImageOffset = BinaryDataSize;
-  TheEntry.ImageSize = OffloadingData.Image->getBufferSize();
+  TheHeader.Size = alignTo(BinaryDataSize + TotalImagesSize, getAlignment());
+  TheHeader.EntriesOffset = sizeof(Header);
+  TheHeader.EntriesCount = EntriesCount;
 
   SmallString<0> Data;
   Data.reserve(TheHeader.Size);
   raw_svector_ostream OS(Data);
   OS << StringRef(reinterpret_cast<char *>(&TheHeader), sizeof(Header));
-  OS << StringRef(reinterpret_cast<char *>(&TheEntry), sizeof(Entry));
-  for (auto &KeyAndValue : OffloadingData.StringData) {
-    uint64_t Offset = sizeof(Header) + sizeof(Entry) + StringEntrySize;
-    StringEntry Map{Offset + StrTab.getOffset(KeyAndValue.first),
-                    Offset + StrTab.getOffset(KeyAndValue.second)};
-    OS << StringRef(reinterpret_cast<char *>(&Map), sizeof(StringEntry));
+
+  // Create the entries using the string table offsets. The string table will 
be
+  // placed directly after the set of entries in memory, and all the images are
+  // after that.
+  uint64_t StringEntryOffset = sizeof(Header) + EntriesSize;
+  uint64_t ImageOffset = BinaryDataSize;
+  for (const OffloadingImage &Img : OffloadingData) {
+    Entry TheEntry;
+
+    TheEntry.TheImageKind = Img.TheImageKind;
+    TheEntry.TheOffloadKind = Img.TheOffloadKind;
+    TheEntry.Flags = Img.Flags;
+
+    TheEntry.StringOffset = StringEntryOffset;
+    StringEntryOffset += sizeof(StringEntry) * Img.StringData.size();
+    TheEntry.NumStrings = Img.StringData.size();
+
+    TheEntry.ImageOffset = ImageOffset;
+    ImageOffset += Img.Image->getBufferSize();
+    TheEntry.ImageSize = Img.Image->getBufferSize();
+
+    OS << StringRef(reinterpret_cast<char *>(&TheEntry), sizeof(Entry));
+  }
+
+  // Create the string map entries.
+  for (const OffloadingImage &Img : OffloadingData) {
+    for (auto &KeyAndValue : Img.StringData) {
+      StringEntry Map{StrTabOffset + StrTab.getOffset(KeyAndValue.first),
+                      StrTabOffset + StrTab.getOffset(KeyAndValue.second),
+                      KeyAndValue.second.size()};
+      OS << StringRef(reinterpret_cast<char *>(&Map), sizeof(StringEntry));
+    }
   }
+
   StrTab.write(OS);
   // Add padding to required image alignment.
-  OS.write_zeros(TheEntry.ImageOffset - OS.tell());
-  OS << OffloadingData.Image->getBuffer();
+  OS.write_zeros(BinaryDataSize - OS.tell());
+
+  for (const OffloadingImage &Img : OffloadingData)
+    OS << Img.Image->getBuffer();
 
   // Add final padding to required alignment.
   assert(TheHeader.Size >= OS.tell() && "Too much data written?");

diff  --git a/llvm/lib/ObjectYAML/OffloadEmitter.cpp 
b/llvm/lib/ObjectYAML/OffloadEmitter.cpp
index 131da68d77506..51167bd812df3 100644
--- a/llvm/lib/ObjectYAML/OffloadEmitter.cpp
+++ b/llvm/lib/ObjectYAML/OffloadEmitter.cpp
@@ -18,6 +18,7 @@ namespace llvm {
 namespace yaml {
 
 bool yaml2offload(Binary &Doc, raw_ostream &Out, ErrorHandler EH) {
+  SmallVector<object::OffloadBinary::OffloadingImage> Images;
   for (const auto &Member : Doc.Members) {
     object::OffloadBinary::OffloadingImage Image{};
     if (Member.ImageKind)
@@ -36,23 +37,24 @@ bool yaml2offload(Binary &Doc, raw_ostream &Out, 
ErrorHandler EH) {
     if (Member.Content)
       Member.Content->writeAsBinary(OS);
     Image.Image = MemoryBuffer::getMemBufferCopy(OS.str());
-
-    // Copy the data to a new buffer so we can modify the bytes directly.
-    auto Buffer = object::OffloadBinary::write(Image);
-    auto *TheHeader =
-        reinterpret_cast<object::OffloadBinary::Header *>(&Buffer[0]);
-    if (Doc.Version)
-      TheHeader->Version = *Doc.Version;
-    if (Doc.Size)
-      TheHeader->Size = *Doc.Size;
-    if (Doc.EntryOffset)
-      TheHeader->EntryOffset = *Doc.EntryOffset;
-    if (Doc.EntrySize)
-      TheHeader->EntrySize = *Doc.EntrySize;
-
-    Out.write(Buffer.begin(), Buffer.size());
+    Images.push_back(std::move(Image));
   }
 
+  // Copy the data to a new buffer so we can modify the bytes directly.
+  auto Buffer = object::OffloadBinary::write(Images);
+  auto *TheHeader =
+      reinterpret_cast<object::OffloadBinary::Header *>(&Buffer[0]);
+  if (Doc.Version)
+    TheHeader->Version = *Doc.Version;
+  if (Doc.Size)
+    TheHeader->Size = *Doc.Size;
+  if (Doc.EntriesOffset)
+    TheHeader->EntriesOffset = *Doc.EntriesOffset;
+  if (Doc.EntriesCount)
+    TheHeader->EntriesCount = *Doc.EntriesCount;
+
+  Out.write(Buffer.begin(), Buffer.size());
+
   return true;
 }
 

diff  --git a/llvm/lib/ObjectYAML/OffloadYAML.cpp 
b/llvm/lib/ObjectYAML/OffloadYAML.cpp
index d5a0edde2179f..c0e0ed41aaca9 100644
--- a/llvm/lib/ObjectYAML/OffloadYAML.cpp
+++ b/llvm/lib/ObjectYAML/OffloadYAML.cpp
@@ -38,6 +38,7 @@ void 
ScalarEnumerationTraits<object::OffloadKind>::enumeration(
   ECase(OFK_OpenMP);
   ECase(OFK_Cuda);
   ECase(OFK_HIP);
+  ECase(OFK_SYCL);
   ECase(OFK_LAST);
 #undef ECase
   IO.enumFallback<Hex16>(Value);
@@ -50,8 +51,8 @@ void MappingTraits<OffloadYAML::Binary>::mapping(IO &IO,
   IO.mapTag("!Offload", true);
   IO.mapOptional("Version", O.Version);
   IO.mapOptional("Size", O.Size);
-  IO.mapOptional("EntryOffset", O.EntryOffset);
-  IO.mapOptional("EntrySize", O.EntrySize);
+  IO.mapOptional("EntriesOffset", O.EntriesOffset);
+  IO.mapOptional("EntriesCount", O.EntriesCount);
   IO.mapRequired("Members", O.Members);
   IO.setContext(nullptr);
 }

diff  --git a/llvm/test/ObjectYAML/Offload/malformed-entry-size.yaml 
b/llvm/test/ObjectYAML/Offload/malformed-entries-count.yaml
similarity index 94%
rename from llvm/test/ObjectYAML/Offload/malformed-entry-size.yaml
rename to llvm/test/ObjectYAML/Offload/malformed-entries-count.yaml
index 3194607ae39a5..bb2a34277963f 100644
--- a/llvm/test/ObjectYAML/Offload/malformed-entry-size.yaml
+++ b/llvm/test/ObjectYAML/Offload/malformed-entries-count.yaml
@@ -1,6 +1,6 @@
 # RUN: yaml2obj %s | not obj2yaml 2>&1 | FileCheck %s
 !Offload
-EntrySize: 999999999
+EntriesCount: 999999999
 Members:
   - ImageKind:        IMG_Cubin
     OffloadKind:      OFK_OpenMP

diff  --git a/llvm/test/ObjectYAML/Offload/malformed-offset.yaml 
b/llvm/test/ObjectYAML/Offload/malformed-offset.yaml
index 03c0431053cce..5aecfffd937bf 100644
--- a/llvm/test/ObjectYAML/Offload/malformed-offset.yaml
+++ b/llvm/test/ObjectYAML/Offload/malformed-offset.yaml
@@ -1,6 +1,6 @@
 # RUN: yaml2obj %s | not obj2yaml 2>&1 | FileCheck %s
 !Offload
-EntryOffset: 999999999
+EntriesOffset: 999999999
 Members:
   - ImageKind:        IMG_Cubin
     OffloadKind:      OFK_OpenMP

diff  --git a/llvm/test/ObjectYAML/Offload/malformed-version.yaml 
b/llvm/test/ObjectYAML/Offload/malformed-version.yaml
index f9279a52e2764..99383491acce0 100644
--- a/llvm/test/ObjectYAML/Offload/malformed-version.yaml
+++ b/llvm/test/ObjectYAML/Offload/malformed-version.yaml
@@ -1,6 +1,6 @@
 # RUN: yaml2obj %s | not obj2yaml 2>&1 | FileCheck %s
 !Offload
-Version: 2
+Version: 3
 Members:
   - ImageKind:        IMG_Cubin
     OffloadKind:      OFK_OpenMP

diff  --git a/llvm/test/ObjectYAML/Offload/multiple_members.yaml 
b/llvm/test/ObjectYAML/Offload/multiple_members.yaml
index ac73d16e429a9..b07f7acd1f782 100644
--- a/llvm/test/ObjectYAML/Offload/multiple_members.yaml
+++ b/llvm/test/ObjectYAML/Offload/multiple_members.yaml
@@ -18,7 +18,14 @@ Members:
       Value:            "amdgcn-amd-amdhsa"
     - Key:              "arch"
       Value:            "gfx908"
-    Content:          "cafefeed"  
+    Content:          "cafefeed"
+  - ImageKind:        IMG_Object
+    OffloadKind:      OFK_SYCL
+    # OIF_Metadata
+    Flags:            1
+    String:
+    - Key:              "device"
+      Value:            "gpu"
 
 # CHECK: --- !Offload
 # CHECK-NEXT: Members:
@@ -40,4 +47,10 @@ Members:
 # CHECK-NEXT:       - Key:             arch
 # CHECK-NEXT:         Value:           gfx908
 # CHECK-NEXT:     Content:         CAFEFEED
+# CHECK-NEXT:   - ImageKind:       IMG_Object
+# CHECK-NEXT:     OffloadKind:     OFK_SYCL
+# CHECK-NEXT:     Flags:           1
+# CHECK-NEXT:     String:
+# CHECK-NEXT:       - Key:             device
+# CHECK-NEXT:         Value:           gpu
 # CHECK-NEXT: ...

diff  --git a/llvm/tools/obj2yaml/offload2yaml.cpp 
b/llvm/tools/obj2yaml/offload2yaml.cpp
index 2b63e1278cd22..47e86f75514c0 100644
--- a/llvm/tools/obj2yaml/offload2yaml.cpp
+++ b/llvm/tools/obj2yaml/offload2yaml.cpp
@@ -16,49 +16,49 @@ using namespace llvm;
 
 namespace {
 
-void populateYAML(OffloadYAML::Binary &YAMLBinary, object::OffloadBinary &OB,
+void populateYAML(OffloadYAML::Binary &YAMLBinary,
+                  ArrayRef<std::unique_ptr<object::OffloadBinary>> OBinaries,
                   UniqueStringSaver Saver) {
-  YAMLBinary.Members.emplace_back();
-  auto &Member = YAMLBinary.Members.back();
-  Member.ImageKind = OB.getImageKind();
-  Member.OffloadKind = OB.getOffloadKind();
-  Member.Flags = OB.getFlags();
-  if (!OB.strings().empty()) {
-    Member.StringEntries = std::vector<OffloadYAML::Binary::StringEntry>();
-    for (const auto &Entry : OB.strings())
-      Member.StringEntries->emplace_back(OffloadYAML::Binary::StringEntry(
-          {Saver.save(Entry.first), Saver.save(Entry.second)}));
+  for (const auto &OBinaryPtr : OBinaries) {
+    object::OffloadBinary &OB = *OBinaryPtr;
+
+    YAMLBinary.Members.emplace_back();
+    auto &Member = YAMLBinary.Members.back();
+    Member.ImageKind = OB.getImageKind();
+    Member.OffloadKind = OB.getOffloadKind();
+    Member.Flags = OB.getFlags();
+    if (!OB.strings().empty()) {
+      Member.StringEntries = std::vector<OffloadYAML::Binary::StringEntry>();
+      for (const auto &StringEntry : OB.strings())
+        Member.StringEntries->emplace_back(OffloadYAML::Binary::StringEntry(
+            {Saver.save(StringEntry.first), Saver.save(StringEntry.second)}));
+    }
+
+    if (!OB.getImage().empty())
+      Member.Content = arrayRefFromStringRef(OB.getImage());
   }
-
-  if (!OB.getImage().empty())
-    Member.Content = arrayRefFromStringRef(OB.getImage());
 }
 
 Expected<OffloadYAML::Binary *> dump(MemoryBufferRef Source,
                                      UniqueStringSaver Saver) {
-  Expected<std::unique_ptr<object::OffloadBinary>> OB =
-      object::OffloadBinary::create(Source);
-  if (!OB)
-    return OB.takeError();
-
   std::unique_ptr<OffloadYAML::Binary> YAMLBinary =
       std::make_unique<OffloadYAML::Binary>();
 
   YAMLBinary->Members = std::vector<OffloadYAML::Binary::Member>();
 
   uint64_t Offset = 0;
-  while (Offset < (*OB)->getMemoryBufferRef().getBufferSize()) {
+  while (Offset < Source.getBufferSize()) {
     MemoryBufferRef Buffer = MemoryBufferRef(
-        (*OB)->getData().drop_front(Offset), (*OB)->getFileName());
-    auto BinaryOrErr = object::OffloadBinary::create(Buffer);
-    if (!BinaryOrErr)
-      return BinaryOrErr.takeError();
-
-    object::OffloadBinary &Binary = **BinaryOrErr;
+        Source.getBuffer().drop_front(Offset), Source.getBufferIdentifier());
+    auto BinariesOrErr = object::OffloadBinary::create(Buffer);
+    if (!BinariesOrErr)
+      return BinariesOrErr.takeError();
 
-    populateYAML(*YAMLBinary, Binary, Saver);
+    SmallVector<std::unique_ptr<object::OffloadBinary>> &Binaries =
+        *BinariesOrErr;
+    populateYAML(*YAMLBinary, Binaries, Saver);
 
-    Offset += Binary.getSize();
+    Offset += Binaries[0]->getSize();
   }
 
   return YAMLBinary.release();

diff  --git a/llvm/unittests/Object/OffloadingTest.cpp 
b/llvm/unittests/Object/OffloadingTest.cpp
index 18c9efaceed06..b6ad6b69f25fc 100644
--- a/llvm/unittests/Object/OffloadingTest.cpp
+++ b/llvm/unittests/Object/OffloadingTest.cpp
@@ -50,7 +50,9 @@ TEST(OffloadingTest, checkOffloadingBinary) {
     FAIL();
 
   // Make sure we get the same data out.
-  auto &Binary = **BinaryOrErr;
+  auto &Binaries = *BinaryOrErr;
+  ASSERT_EQ(Binaries.size(), 1u);
+  auto &Binary = *Binaries[0];
   ASSERT_EQ(Data.TheImageKind, Binary.getImageKind());
   ASSERT_EQ(Data.TheOffloadKind, Binary.getOffloadKind());
   ASSERT_EQ(Data.Flags, Binary.getFlags());
@@ -65,3 +67,209 @@ TEST(OffloadingTest, checkOffloadingBinary) {
   EXPECT_TRUE(Binary.getSize() % OffloadBinary::getAlignment() == 0);
   EXPECT_TRUE(Binary.getSize() == BinaryBuffer->getBuffer().size());
 }
+
+static std::unique_ptr<MemoryBuffer>
+createMultiEntryBinary(size_t NumEntries,
+                       SmallVectorImpl<std::string> &StringStorage) {
+  // Reserve space to prevent reallocation which would invalidate StringRefs.
+  // Each entry needs: "id", id_value, "arch", arch_value, image_content = 5
+  // strings.
+  StringStorage.reserve(NumEntries * 5);
+
+  SmallVector<OffloadBinary::OffloadingImage> Images;
+
+  for (size_t i = 0; i < NumEntries; ++i) {
+    OffloadBinary::OffloadingImage Data;
+    Data.TheImageKind = static_cast<ImageKind>(i % IMG_LAST);
+    Data.TheOffloadKind = static_cast<OffloadKind>(i % OFK_LAST);
+
+    MapVector<StringRef, StringRef> StringData;
+
+    StringStorage.push_back("id");
+    StringStorage.push_back(std::to_string(i));
+    StringData[StringStorage[StringStorage.size() - 2]] =
+        StringStorage[StringStorage.size() - 1];
+
+    StringStorage.push_back("arch");
+    StringStorage.push_back("gpu" + std::to_string(i));
+    StringData[StringStorage[StringStorage.size() - 2]] =
+        StringStorage[StringStorage.size() - 1];
+
+    Data.StringData = StringData;
+
+    // Make the last entry metadata-only (no image)
+    if (i == NumEntries - 1) {
+      Data.Flags = OIF_Metadata;
+      Data.Image = MemoryBuffer::getMemBuffer("", "", false);
+    } else {
+      Data.Flags = i * 100;
+      StringStorage.push_back("ImageData" + std::to_string(i));
+      Data.Image = MemoryBuffer::getMemBuffer(StringStorage.back(), "", false);
+    }
+
+    Images.push_back(std::move(Data));
+  }
+
+  return MemoryBuffer::getMemBufferCopy(OffloadBinary::write(Images));
+}
+
+// Test multi-entry binaries and extraction without index (get all entries).
+TEST(OffloadingTest, checkMultiEntryBinaryExtraction) {
+  const size_t NumEntries = 5;
+  SmallVector<std::string> StringStorage;
+  auto BinaryBuffer = createMultiEntryBinary(NumEntries, StringStorage);
+
+  // Test extracting all entries (no index).
+  auto BinariesOrErr = OffloadBinary::create(*BinaryBuffer);
+  ASSERT_THAT_EXPECTED(BinariesOrErr, Succeeded());
+
+  auto &Binaries = *BinariesOrErr;
+  ASSERT_EQ(Binaries.size(), NumEntries)
+      << "Expected all entries when no index provided";
+
+  // Verify each entry.
+  for (size_t i = 0; i < NumEntries; ++i) {
+    auto &Binary = *Binaries[i];
+    EXPECT_EQ(Binary.getImageKind(), static_cast<ImageKind>(i % IMG_LAST));
+    EXPECT_EQ(Binary.getOffloadKind(), static_cast<OffloadKind>(i % OFK_LAST));
+    EXPECT_EQ(Binary.getIndex(), i);
+
+    std::string ExpectedId = std::to_string(i);
+    std::string ExpectedArch = "gpu" + std::to_string(i);
+    EXPECT_EQ(Binary.getString("id"), ExpectedId);
+    EXPECT_EQ(Binary.getString("arch"), ExpectedArch);
+
+    // Last entry is metadata-only.
+    if (i == NumEntries - 1) {
+      EXPECT_EQ(Binary.getFlags(), OIF_Metadata);
+      EXPECT_TRUE(Binary.getImage().empty());
+    } else {
+      EXPECT_EQ(Binary.getFlags(), i * 100);
+      std::string ExpectedImage = "ImageData" + std::to_string(i);
+      EXPECT_EQ(Binary.getImage(), ExpectedImage);
+    }
+  }
+
+  // Ensure the size and alignment of the data is correct.
+  EXPECT_TRUE(Binaries[0]->getSize() % OffloadBinary::getAlignment() == 0);
+  EXPECT_TRUE(Binaries[0]->getSize() == BinaryBuffer->getBuffer().size());
+}
+
+// Test index-based extraction from multi-entry binary.
+TEST(OffloadingTest, checkIndexBasedExtraction) {
+  const size_t NumEntries = 5;
+  SmallVector<std::string> StringStorage;
+  auto BinaryBuffer = createMultiEntryBinary(NumEntries, StringStorage);
+
+  // Test extracting specific indices.
+  for (uint64_t i = 0; i < NumEntries; ++i) {
+    auto BinariesOrErr = OffloadBinary::create(*BinaryBuffer, i);
+    ASSERT_THAT_EXPECTED(BinariesOrErr, Succeeded());
+
+    auto &Binaries = *BinariesOrErr;
+    ASSERT_EQ(Binaries.size(), 1u) << "Expected single entry when using index";
+
+    auto &Binary = *Binaries[0];
+    EXPECT_EQ(Binary.getImageKind(), static_cast<ImageKind>(i % IMG_LAST));
+    EXPECT_EQ(Binary.getOffloadKind(), static_cast<OffloadKind>(i % OFK_LAST));
+    EXPECT_EQ(Binary.getIndex(), i);
+
+    std::string ExpectedId = std::to_string(i);
+    std::string ExpectedArch = "gpu" + std::to_string(i);
+    EXPECT_EQ(Binary.getString("id"), ExpectedId);
+    EXPECT_EQ(Binary.getString("arch"), ExpectedArch);
+
+    // Last entry is metadata-only.
+    if (i == NumEntries - 1) {
+      EXPECT_EQ(Binary.getFlags(), OIF_Metadata);
+      EXPECT_TRUE(Binary.getImage().empty());
+    } else {
+      EXPECT_EQ(Binary.getFlags(), i * 100);
+      std::string ExpectedImage = "ImageData" + std::to_string(i);
+      EXPECT_EQ(Binary.getImage(), ExpectedImage);
+    }
+  }
+
+  // Test out-of-bounds index.
+  auto OutOfBoundsOrErr = OffloadBinary::create(*BinaryBuffer, NumEntries + 
10);
+  EXPECT_THAT_EXPECTED(OutOfBoundsOrErr, Failed());
+}
+
+TEST(OffloadingTest, checkEdgeCases) {
+  // Test with empty string data.
+  {
+    OffloadBinary::OffloadingImage Data;
+    Data.TheImageKind = IMG_Object;
+    Data.TheOffloadKind = OFK_OpenMP;
+    Data.Flags = 0;
+    Data.StringData = MapVector<StringRef, StringRef>(); // Empty
+
+    std::string ImageContent = "TestImage";
+    Data.Image = MemoryBuffer::getMemBuffer(ImageContent, "", false);
+
+    auto BinaryBuffer =
+        MemoryBuffer::getMemBufferCopy(OffloadBinary::write(Data));
+    auto BinariesOrErr = OffloadBinary::create(*BinaryBuffer);
+    ASSERT_THAT_EXPECTED(BinariesOrErr, Succeeded());
+
+    auto &Binaries = *BinariesOrErr;
+    ASSERT_EQ(Binaries.size(), 1u);
+    EXPECT_TRUE(Binaries[0]->strings().empty());
+    EXPECT_EQ(Binaries[0]->getImage(), ImageContent);
+  }
+
+  // Test with empty image data.
+  {
+    std::string Key = "test";
+    std::string Value = "value";
+
+    OffloadBinary::OffloadingImage Data;
+    Data.TheImageKind = IMG_Object;
+    Data.TheOffloadKind = OFK_SYCL;
+    Data.Flags = 0;
+
+    MapVector<StringRef, StringRef> StringData;
+    StringData[Key] = Value;
+    Data.StringData = StringData;
+
+    Data.Image = MemoryBuffer::getMemBuffer("", "", false); // Empty image
+
+    auto BinaryBuffer =
+        MemoryBuffer::getMemBufferCopy(OffloadBinary::write(Data));
+    auto BinariesOrErr = OffloadBinary::create(*BinaryBuffer);
+    ASSERT_THAT_EXPECTED(BinariesOrErr, Succeeded());
+
+    auto &Binaries = *BinariesOrErr;
+    ASSERT_EQ(Binaries.size(), 1u);
+    EXPECT_TRUE(Binaries[0]->getImage().empty());
+    EXPECT_EQ(Binaries[0]->getString("test"), "value");
+  }
+
+  // Test with large string values.
+  {
+    std::string Key = "large_key";
+    std::string LargeValue(4096, 'X'); // Large value
+    std::string ImageContent = "Image";
+
+    OffloadBinary::OffloadingImage Data;
+    Data.TheImageKind = IMG_Bitcode;
+    Data.TheOffloadKind = OFK_OpenMP;
+    Data.Flags = 0;
+
+    MapVector<StringRef, StringRef> StringData;
+    StringData[Key] = LargeValue;
+    Data.StringData = StringData;
+
+    Data.Image = MemoryBuffer::getMemBuffer(ImageContent, "", false);
+
+    auto BinaryBuffer =
+        MemoryBuffer::getMemBufferCopy(OffloadBinary::write(Data));
+    auto BinariesOrErr = OffloadBinary::create(*BinaryBuffer);
+    ASSERT_THAT_EXPECTED(BinariesOrErr, Succeeded());
+
+    auto &Binaries = *BinariesOrErr;
+    ASSERT_EQ(Binaries.size(), 1u);
+    EXPECT_EQ(Binaries[0]->getString("large_key"), LargeValue);
+    EXPECT_EQ(Binaries[0]->getString("large_key").size(), 4096u);
+  }
+}


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

Reply via email to