1e471ba3dSJoseph Huber //===- Offloading.cpp - Utilities for handling offloading code -*- C++ -*-===//
2e471ba3dSJoseph Huber //
3e471ba3dSJoseph Huber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4e471ba3dSJoseph Huber // See https://llvm.org/LICENSE.txt for license information.
5e471ba3dSJoseph Huber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6e471ba3dSJoseph Huber //
7e471ba3dSJoseph Huber //===----------------------------------------------------------------------===//
8e471ba3dSJoseph Huber
9e471ba3dSJoseph Huber #include "llvm/Object/OffloadBinary.h"
10e471ba3dSJoseph Huber
11e471ba3dSJoseph Huber #include "llvm/ADT/StringSwitch.h"
12afd2f7e9SJoseph Huber #include "llvm/BinaryFormat/Magic.h"
13e471ba3dSJoseph Huber #include "llvm/MC/StringTableBuilder.h"
14e471ba3dSJoseph Huber #include "llvm/Object/Error.h"
15*ccf7dd5eSJoseph Huber #include "llvm/Support/Alignment.h"
16e471ba3dSJoseph Huber #include "llvm/Support/FileOutputBuffer.h"
17e471ba3dSJoseph Huber
182108f7a2SFangrui Song using namespace llvm;
192108f7a2SFangrui Song using namespace llvm::object;
20afd2f7e9SJoseph Huber
21e471ba3dSJoseph Huber Expected<std::unique_ptr<OffloadBinary>>
create(MemoryBufferRef Buf)22e471ba3dSJoseph Huber OffloadBinary::create(MemoryBufferRef Buf) {
23e471ba3dSJoseph Huber if (Buf.getBufferSize() < sizeof(Header) + sizeof(Entry))
24afd2f7e9SJoseph Huber return errorCodeToError(object_error::parse_failed);
25e471ba3dSJoseph Huber
26e471ba3dSJoseph Huber // Check for 0x10FF1OAD magic bytes.
27afd2f7e9SJoseph Huber if (identify_magic(Buf.getBuffer()) != file_magic::offload_binary)
28afd2f7e9SJoseph Huber return errorCodeToError(object_error::parse_failed);
29e471ba3dSJoseph Huber
30*ccf7dd5eSJoseph Huber // Make sure that the data has sufficient alignment.
31*ccf7dd5eSJoseph Huber if (!isAddrAligned(Align(getAlignment()), Buf.getBufferStart()))
32*ccf7dd5eSJoseph Huber return errorCodeToError(object_error::parse_failed);
33*ccf7dd5eSJoseph Huber
34e471ba3dSJoseph Huber const char *Start = Buf.getBufferStart();
35e471ba3dSJoseph Huber const Header *TheHeader = reinterpret_cast<const Header *>(Start);
361dcbe03cSJoseph Huber if (TheHeader->Version != OffloadBinary::Version)
371dcbe03cSJoseph Huber return errorCodeToError(object_error::parse_failed);
381dcbe03cSJoseph Huber
391dcbe03cSJoseph Huber if (TheHeader->Size > Buf.getBufferSize() ||
401dcbe03cSJoseph Huber TheHeader->EntryOffset > TheHeader->Size - sizeof(Entry) ||
411dcbe03cSJoseph Huber TheHeader->EntrySize > TheHeader->Size - sizeof(Header))
421dcbe03cSJoseph Huber return errorCodeToError(object_error::unexpected_eof);
431dcbe03cSJoseph Huber
44e471ba3dSJoseph Huber const Entry *TheEntry =
45e471ba3dSJoseph Huber reinterpret_cast<const Entry *>(&Start[TheHeader->EntryOffset]);
46e471ba3dSJoseph Huber
471dcbe03cSJoseph Huber if (TheEntry->ImageOffset > Buf.getBufferSize() ||
484e2a0092SJoseph Huber TheEntry->StringOffset > Buf.getBufferSize())
494e2a0092SJoseph Huber return errorCodeToError(object_error::unexpected_eof);
504e2a0092SJoseph Huber
51e471ba3dSJoseph Huber return std::unique_ptr<OffloadBinary>(
52afd2f7e9SJoseph Huber new OffloadBinary(Buf, TheHeader, TheEntry));
53e471ba3dSJoseph Huber }
54e471ba3dSJoseph Huber
55e471ba3dSJoseph Huber std::unique_ptr<MemoryBuffer>
write(const OffloadingImage & OffloadingData)56e471ba3dSJoseph Huber OffloadBinary::write(const OffloadingImage &OffloadingData) {
57e471ba3dSJoseph Huber // Create a null-terminated string table with all the used strings.
58e471ba3dSJoseph Huber StringTableBuilder StrTab(StringTableBuilder::ELF);
59e471ba3dSJoseph Huber for (auto &KeyAndValue : OffloadingData.StringData) {
60e471ba3dSJoseph Huber StrTab.add(KeyAndValue.getKey());
61e471ba3dSJoseph Huber StrTab.add(KeyAndValue.getValue());
62e471ba3dSJoseph Huber }
63e471ba3dSJoseph Huber StrTab.finalize();
64e471ba3dSJoseph Huber
65e471ba3dSJoseph Huber uint64_t StringEntrySize =
66e471ba3dSJoseph Huber sizeof(StringEntry) * OffloadingData.StringData.size();
67e471ba3dSJoseph Huber
689db2f323SJoseph Huber // Make sure the image we're wrapping around is aligned as well.
699db2f323SJoseph Huber uint64_t BinaryDataSize = alignTo(sizeof(Header) + sizeof(Entry) +
709db2f323SJoseph Huber StringEntrySize + StrTab.getSize(),
719db2f323SJoseph Huber getAlignment());
729db2f323SJoseph Huber
73e471ba3dSJoseph Huber // Create the header and fill in the offsets. The entry will be directly
74e471ba3dSJoseph Huber // placed after the header in memory. Align the size to the alignment of the
75e471ba3dSJoseph Huber // header so this can be placed contiguously in a single section.
76e471ba3dSJoseph Huber Header TheHeader;
779db2f323SJoseph Huber TheHeader.Size = alignTo(
78f06731e3SJoseph Huber BinaryDataSize + OffloadingData.Image->getBufferSize(), getAlignment());
79e471ba3dSJoseph Huber TheHeader.EntryOffset = sizeof(Header);
80e471ba3dSJoseph Huber TheHeader.EntrySize = sizeof(Entry);
81e471ba3dSJoseph Huber
82e471ba3dSJoseph Huber // Create the entry using the string table offsets. The string table will be
83e471ba3dSJoseph Huber // placed directly after the entry in memory, and the image after that.
84e471ba3dSJoseph Huber Entry TheEntry;
85e471ba3dSJoseph Huber TheEntry.TheImageKind = OffloadingData.TheImageKind;
86e471ba3dSJoseph Huber TheEntry.TheOffloadKind = OffloadingData.TheOffloadKind;
87e471ba3dSJoseph Huber TheEntry.Flags = OffloadingData.Flags;
88e471ba3dSJoseph Huber TheEntry.StringOffset = sizeof(Header) + sizeof(Entry);
89e471ba3dSJoseph Huber TheEntry.NumStrings = OffloadingData.StringData.size();
90e471ba3dSJoseph Huber
919db2f323SJoseph Huber TheEntry.ImageOffset = BinaryDataSize;
92f06731e3SJoseph Huber TheEntry.ImageSize = OffloadingData.Image->getBufferSize();
93e471ba3dSJoseph Huber
944e2a0092SJoseph Huber SmallVector<char> Data;
954e2a0092SJoseph Huber Data.reserve(TheHeader.Size);
96e471ba3dSJoseph Huber raw_svector_ostream OS(Data);
97e471ba3dSJoseph Huber OS << StringRef(reinterpret_cast<char *>(&TheHeader), sizeof(Header));
98e471ba3dSJoseph Huber OS << StringRef(reinterpret_cast<char *>(&TheEntry), sizeof(Entry));
99e471ba3dSJoseph Huber for (auto &KeyAndValue : OffloadingData.StringData) {
100e471ba3dSJoseph Huber uint64_t Offset = sizeof(Header) + sizeof(Entry) + StringEntrySize;
101e471ba3dSJoseph Huber StringEntry Map{Offset + StrTab.getOffset(KeyAndValue.getKey()),
102e471ba3dSJoseph Huber Offset + StrTab.getOffset(KeyAndValue.getValue())};
103e471ba3dSJoseph Huber OS << StringRef(reinterpret_cast<char *>(&Map), sizeof(StringEntry));
104e471ba3dSJoseph Huber }
105e471ba3dSJoseph Huber StrTab.write(OS);
1069db2f323SJoseph Huber // Add padding to required image alignment.
1079db2f323SJoseph Huber OS.write_zeros(TheEntry.ImageOffset - OS.tell());
108f06731e3SJoseph Huber OS << OffloadingData.Image->getBuffer();
109e471ba3dSJoseph Huber
110e471ba3dSJoseph Huber // Add final padding to required alignment.
111e471ba3dSJoseph Huber assert(TheHeader.Size >= OS.tell() && "Too much data written?");
112e471ba3dSJoseph Huber OS.write_zeros(TheHeader.Size - OS.tell());
113e471ba3dSJoseph Huber assert(TheHeader.Size == OS.tell() && "Size mismatch");
114e471ba3dSJoseph Huber
115e471ba3dSJoseph Huber return MemoryBuffer::getMemBufferCopy(OS.str());
116e471ba3dSJoseph Huber }
117e471ba3dSJoseph Huber
getOffloadKind(StringRef Name)1182108f7a2SFangrui Song OffloadKind object::getOffloadKind(StringRef Name) {
119e471ba3dSJoseph Huber return llvm::StringSwitch<OffloadKind>(Name)
120e471ba3dSJoseph Huber .Case("openmp", OFK_OpenMP)
121e471ba3dSJoseph Huber .Case("cuda", OFK_Cuda)
122e471ba3dSJoseph Huber .Case("hip", OFK_HIP)
123e471ba3dSJoseph Huber .Default(OFK_None);
124e471ba3dSJoseph Huber }
125e471ba3dSJoseph Huber
getOffloadKindName(OffloadKind Kind)1262108f7a2SFangrui Song StringRef object::getOffloadKindName(OffloadKind Kind) {
127e471ba3dSJoseph Huber switch (Kind) {
128e471ba3dSJoseph Huber case OFK_OpenMP:
129e471ba3dSJoseph Huber return "openmp";
130e471ba3dSJoseph Huber case OFK_Cuda:
131e471ba3dSJoseph Huber return "cuda";
132e471ba3dSJoseph Huber case OFK_HIP:
133e471ba3dSJoseph Huber return "hip";
134e471ba3dSJoseph Huber default:
135e471ba3dSJoseph Huber return "none";
136e471ba3dSJoseph Huber }
137e471ba3dSJoseph Huber }
138e471ba3dSJoseph Huber
getImageKind(StringRef Name)1392108f7a2SFangrui Song ImageKind object::getImageKind(StringRef Name) {
140e471ba3dSJoseph Huber return llvm::StringSwitch<ImageKind>(Name)
141e471ba3dSJoseph Huber .Case("o", IMG_Object)
142e471ba3dSJoseph Huber .Case("bc", IMG_Bitcode)
143e471ba3dSJoseph Huber .Case("cubin", IMG_Cubin)
144e471ba3dSJoseph Huber .Case("fatbin", IMG_Fatbinary)
145e471ba3dSJoseph Huber .Case("s", IMG_PTX)
146e471ba3dSJoseph Huber .Default(IMG_None);
147e471ba3dSJoseph Huber }
148e471ba3dSJoseph Huber
getImageKindName(ImageKind Kind)1492108f7a2SFangrui Song StringRef object::getImageKindName(ImageKind Kind) {
150e471ba3dSJoseph Huber switch (Kind) {
151e471ba3dSJoseph Huber case IMG_Object:
152e471ba3dSJoseph Huber return "o";
153e471ba3dSJoseph Huber case IMG_Bitcode:
154e471ba3dSJoseph Huber return "bc";
155e471ba3dSJoseph Huber case IMG_Cubin:
156e471ba3dSJoseph Huber return "cubin";
157e471ba3dSJoseph Huber case IMG_Fatbinary:
158e471ba3dSJoseph Huber return "fatbin";
159e471ba3dSJoseph Huber case IMG_PTX:
160e471ba3dSJoseph Huber return "s";
161e471ba3dSJoseph Huber default:
162e471ba3dSJoseph Huber return "";
163e471ba3dSJoseph Huber }
164e471ba3dSJoseph Huber }
165