1 //===-- clang-offload-packager/ClangOffloadPackager.cpp - file bundler ---===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===---------------------------------------------------------------------===//
9 // This tool takes several device object files and bundles them into a single
10 // binary image using a custom binary format. This is intended to be used to
11 // embed many device files into an application to create a fat binary.
13 //===---------------------------------------------------------------------===//
15 #include "clang/Basic/Version.h"
17 #include "llvm/BinaryFormat/Magic.h"
18 #include "llvm/Object/OffloadBinary.h"
19 #include "llvm/Support/CommandLine.h"
20 #include "llvm/Support/FileOutputBuffer.h"
21 #include "llvm/Support/FileSystem.h"
22 #include "llvm/Support/MemoryBuffer.h"
23 #include "llvm/Support/Path.h"
24 #include "llvm/Support/Signals.h"
25 #include "llvm/Support/StringSaver.h"
26 #include "llvm/Support/WithColor.h"
29 using namespace llvm::object
;
31 static cl::opt
<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden
);
33 static cl::OptionCategory
34 ClangOffloadPackagerCategory("clang-offload-packager options");
36 static cl::opt
<std::string
> OutputFile("o", cl::desc("Write output to <file>."),
37 cl::value_desc("file"),
38 cl::cat(ClangOffloadPackagerCategory
));
40 static cl::opt
<std::string
> InputFile(cl::Positional
,
41 cl::desc("Extract from <file>."),
42 cl::value_desc("file"),
43 cl::cat(ClangOffloadPackagerCategory
));
45 static cl::list
<std::string
>
47 cl::desc("List of key and value arguments. Required keywords "
48 "are 'file' and 'triple'."),
49 cl::value_desc("<key>=<value>,..."),
50 cl::cat(ClangOffloadPackagerCategory
));
52 static void PrintVersion(raw_ostream
&OS
) {
53 OS
<< clang::getClangToolFullVersion("clang-offload-packager") << '\n';
56 // Get a map containing all the arguments for the image. Repeated arguments will
57 // be placed in a comma separated list.
58 static DenseMap
<StringRef
, StringRef
> getImageArguments(StringRef Image
,
60 DenseMap
<StringRef
, StringRef
> Args
;
61 for (StringRef Arg
: llvm::split(Image
, ",")) {
62 auto [Key
, Value
] = Arg
.split("=");
64 Args
[Key
] = Saver
.save(Args
[Key
] + "," + Value
);
72 static Error
bundleImages() {
73 SmallVector
<char, 1024> BinaryData
;
74 raw_svector_ostream
OS(BinaryData
);
75 for (StringRef Image
: DeviceImages
) {
76 BumpPtrAllocator Alloc
;
77 StringSaver
Saver(Alloc
);
78 DenseMap
<StringRef
, StringRef
> Args
= getImageArguments(Image
, Saver
);
80 if (!Args
.count("triple") || !Args
.count("file"))
81 return createStringError(
82 inconvertibleErrorCode(),
83 "'file' and 'triple' are required image arguments");
85 OffloadBinary::OffloadingImage ImageBinary
{};
86 std::unique_ptr
<llvm::MemoryBuffer
> DeviceImage
;
87 for (const auto &[Key
, Value
] : Args
) {
89 llvm::ErrorOr
<std::unique_ptr
<llvm::MemoryBuffer
>> ObjectOrErr
=
90 llvm::MemoryBuffer::getFileOrSTDIN(Value
);
91 if (std::error_code EC
= ObjectOrErr
.getError())
92 return errorCodeToError(EC
);
94 // Clang uses the '.o' suffix for LTO bitcode.
95 if (identify_magic((*ObjectOrErr
)->getBuffer()) == file_magic::bitcode
)
96 ImageBinary
.TheImageKind
= object::IMG_Bitcode
;
98 ImageBinary
.TheImageKind
=
99 getImageKind(sys::path::extension(Value
).drop_front());
100 ImageBinary
.Image
= std::move(*ObjectOrErr
);
101 } else if (Key
== "kind") {
102 ImageBinary
.TheOffloadKind
= getOffloadKind(Value
);
104 ImageBinary
.StringData
[Key
] = Value
;
107 std::unique_ptr
<MemoryBuffer
> Buffer
= OffloadBinary::write(ImageBinary
);
108 if (Buffer
->getBufferSize() % OffloadBinary::getAlignment() != 0)
109 return createStringError(inconvertibleErrorCode(),
110 "Offload binary has invalid size alignment");
111 OS
<< Buffer
->getBuffer();
114 Expected
<std::unique_ptr
<FileOutputBuffer
>> OutputOrErr
=
115 FileOutputBuffer::create(OutputFile
, BinaryData
.size());
117 return OutputOrErr
.takeError();
118 std::unique_ptr
<FileOutputBuffer
> Output
= std::move(*OutputOrErr
);
119 std::copy(BinaryData
.begin(), BinaryData
.end(), Output
->getBufferStart());
120 if (Error E
= Output
->commit())
122 return Error::success();
125 static Error
unbundleImages() {
126 ErrorOr
<std::unique_ptr
<MemoryBuffer
>> BufferOrErr
=
127 MemoryBuffer::getFileOrSTDIN(InputFile
);
128 if (std::error_code EC
= BufferOrErr
.getError())
129 return createFileError(InputFile
, EC
);
130 std::unique_ptr
<MemoryBuffer
> Buffer
= std::move(*BufferOrErr
);
132 // This data can be misaligned if extracted from an archive.
133 if (!isAddrAligned(Align(OffloadBinary::getAlignment()),
134 Buffer
->getBufferStart()))
135 Buffer
= MemoryBuffer::getMemBufferCopy(Buffer
->getBuffer(),
136 Buffer
->getBufferIdentifier());
138 SmallVector
<OffloadFile
> Binaries
;
139 if (Error Err
= extractOffloadBinaries(*Buffer
, Binaries
))
142 // Try to extract each device image specified by the user from the input file.
143 for (StringRef Image
: DeviceImages
) {
144 BumpPtrAllocator Alloc
;
145 StringSaver
Saver(Alloc
);
146 auto Args
= getImageArguments(Image
, Saver
);
148 for (uint64_t I
= 0, E
= Binaries
.size(); I
!= E
; ++I
) {
149 const auto *Binary
= Binaries
[I
].getBinary();
150 // We handle the 'file' and 'kind' identifiers differently.
151 bool Match
= llvm::all_of(Args
, [&](auto &Arg
) {
152 const auto [Key
, Value
] = Arg
;
156 return Binary
->getOffloadKind() == getOffloadKind(Value
);
157 return Binary
->getString(Key
) == Value
;
162 // If the user did not provide a filename derive one from the input and
166 ? Saver
.save(sys::path::stem(InputFile
) + "-" +
167 Binary
->getTriple() + "-" + Binary
->getArch() + "." +
168 std::to_string(I
) + "." +
169 getImageKindName(Binary
->getImageKind()))
172 Expected
<std::unique_ptr
<FileOutputBuffer
>> OutputOrErr
=
173 FileOutputBuffer::create(Filename
, Binary
->getImage().size());
175 return OutputOrErr
.takeError();
176 std::unique_ptr
<FileOutputBuffer
> Output
= std::move(*OutputOrErr
);
177 llvm::copy(Binary
->getImage(), Output
->getBufferStart());
178 if (Error E
= Output
->commit())
183 return Error::success();
186 int main(int argc
, const char **argv
) {
187 sys::PrintStackTraceOnErrorSignal(argv
[0]);
188 cl::HideUnrelatedOptions(ClangOffloadPackagerCategory
);
189 cl::SetVersionPrinter(PrintVersion
);
190 cl::ParseCommandLineOptions(
192 "A utility for bundling several object files into a single binary.\n"
193 "The output binary can then be embedded into the host section table\n"
194 "to create a fatbinary containing offloading code.\n");
197 cl::PrintHelpMessage();
201 auto reportError
= [argv
](Error E
) {
202 logAllUnhandledErrors(std::move(E
), WithColor::error(errs(), argv
[0]));
206 if (!InputFile
.empty() && !OutputFile
.empty())
208 createStringError(inconvertibleErrorCode(),
209 "Packaging to an output file and extracting from an "
210 "input file are mutually exclusive."));
212 if (!OutputFile
.empty()) {
213 if (Error Err
= bundleImages())
214 return reportError(std::move(Err
));
215 } else if (!InputFile
.empty()) {
216 if (Error Err
= unbundleImages())
217 return reportError(std::move(Err
));