1 //===-- clang-offload-bundler/ClangOffloadBundler.cpp ---------------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// This file implements a clang-offload-bundler that bundles different
11 /// files that relate with the same source code but different targets into a
12 /// single one. Also the implements the opposite functionality, i.e. unbundle
13 /// files previous created by this tool.
14 ///
15 //===----------------------------------------------------------------------===//
16
17 #include "clang/Basic/Version.h"
18 #include "llvm/ADT/ArrayRef.h"
19 #include "llvm/ADT/SmallString.h"
20 #include "llvm/ADT/SmallVector.h"
21 #include "llvm/ADT/StringMap.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/ADT/StringSwitch.h"
24 #include "llvm/ADT/Triple.h"
25 #include "llvm/Object/Binary.h"
26 #include "llvm/Object/ObjectFile.h"
27 #include "llvm/Support/Casting.h"
28 #include "llvm/Support/CommandLine.h"
29 #include "llvm/Support/Errc.h"
30 #include "llvm/Support/Error.h"
31 #include "llvm/Support/ErrorOr.h"
32 #include "llvm/Support/FileSystem.h"
33 #include "llvm/Support/MemoryBuffer.h"
34 #include "llvm/Support/Path.h"
35 #include "llvm/Support/Program.h"
36 #include "llvm/Support/Signals.h"
37 #include "llvm/Support/StringSaver.h"
38 #include "llvm/Support/WithColor.h"
39 #include "llvm/Support/raw_ostream.h"
40 #include <algorithm>
41 #include <cassert>
42 #include <cstddef>
43 #include <cstdint>
44 #include <forward_list>
45 #include <memory>
46 #include <set>
47 #include <string>
48 #include <system_error>
49 #include <utility>
50
51 using namespace llvm;
52 using namespace llvm::object;
53
54 static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
55
56 // Mark all our options with this category, everything else (except for -version
57 // and -help) will be hidden.
58 static cl::OptionCategory
59 ClangOffloadBundlerCategory("clang-offload-bundler options");
60
61 static cl::list<std::string>
62 InputFileNames("inputs", cl::CommaSeparated, cl::OneOrMore,
63 cl::desc("[<input file>,...]"),
64 cl::cat(ClangOffloadBundlerCategory));
65 static cl::list<std::string>
66 OutputFileNames("outputs", cl::CommaSeparated,
67 cl::desc("[<output file>,...]"),
68 cl::cat(ClangOffloadBundlerCategory));
69 static cl::list<std::string>
70 TargetNames("targets", cl::CommaSeparated,
71 cl::desc("[<offload kind>-<target triple>,...]"),
72 cl::cat(ClangOffloadBundlerCategory));
73 static cl::opt<std::string>
74 FilesType("type", cl::Required,
75 cl::desc("Type of the files to be bundled/unbundled.\n"
76 "Current supported types are:\n"
77 " i - cpp-output\n"
78 " ii - c++-cpp-output\n"
79 " cui - cuda/hip-output\n"
80 " d - dependency\n"
81 " ll - llvm\n"
82 " bc - llvm-bc\n"
83 " s - assembler\n"
84 " o - object\n"
85 " gch - precompiled-header\n"
86 " ast - clang AST file"),
87 cl::cat(ClangOffloadBundlerCategory));
88 static cl::opt<bool>
89 Unbundle("unbundle",
90 cl::desc("Unbundle bundled file into several output files.\n"),
91 cl::init(false), cl::cat(ClangOffloadBundlerCategory));
92
93 static cl::opt<bool>
94 ListBundleIDs("list", cl::desc("List bundle IDs in the bundled file.\n"),
95 cl::init(false), cl::cat(ClangOffloadBundlerCategory));
96
97 static cl::opt<bool> PrintExternalCommands(
98 "###",
99 cl::desc("Print any external commands that are to be executed "
100 "instead of actually executing them - for testing purposes.\n"),
101 cl::init(false), cl::cat(ClangOffloadBundlerCategory));
102
103 static cl::opt<bool>
104 AllowMissingBundles("allow-missing-bundles",
105 cl::desc("Create empty files if bundles are missing "
106 "when unbundling.\n"),
107 cl::init(false), cl::cat(ClangOffloadBundlerCategory));
108
109 static cl::opt<unsigned>
110 BundleAlignment("bundle-align",
111 cl::desc("Alignment of bundle for binary files"),
112 cl::init(1), cl::cat(ClangOffloadBundlerCategory));
113
114 /// Magic string that marks the existence of offloading data.
115 #define OFFLOAD_BUNDLER_MAGIC_STR "__CLANG_OFFLOAD_BUNDLE__"
116
117 /// The index of the host input in the list of inputs.
118 static unsigned HostInputIndex = ~0u;
119
120 /// Path to the current binary.
121 static std::string BundlerExecutable;
122
123 /// Obtain the offload kind and real machine triple out of the target
124 /// information specified by the user.
getOffloadKindAndTriple(StringRef Target,StringRef & OffloadKind,StringRef & Triple)125 static void getOffloadKindAndTriple(StringRef Target, StringRef &OffloadKind,
126 StringRef &Triple) {
127 auto KindTriplePair = Target.split('-');
128 OffloadKind = KindTriplePair.first;
129 Triple = KindTriplePair.second;
130 }
hasHostKind(StringRef Target)131 static bool hasHostKind(StringRef Target) {
132 StringRef OffloadKind;
133 StringRef Triple;
134 getOffloadKindAndTriple(Target, OffloadKind, Triple);
135 return OffloadKind == "host";
136 }
137
138 /// Generic file handler interface.
139 class FileHandler {
140 public:
141 struct BundleInfo {
142 StringRef BundleID;
143 };
144
FileHandler()145 FileHandler() {}
146
~FileHandler()147 virtual ~FileHandler() {}
148
149 /// Update the file handler with information from the header of the bundled
150 /// file.
151 virtual Error ReadHeader(MemoryBuffer &Input) = 0;
152
153 /// Read the marker of the next bundled to be read in the file. The bundle
154 /// name is returned if there is one in the file, or `None` if there are no
155 /// more bundles to be read.
156 virtual Expected<Optional<StringRef>>
157 ReadBundleStart(MemoryBuffer &Input) = 0;
158
159 /// Read the marker that closes the current bundle.
160 virtual Error ReadBundleEnd(MemoryBuffer &Input) = 0;
161
162 /// Read the current bundle and write the result into the stream \a OS.
163 virtual Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0;
164
165 /// Write the header of the bundled file to \a OS based on the information
166 /// gathered from \a Inputs.
167 virtual Error WriteHeader(raw_fd_ostream &OS,
168 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) = 0;
169
170 /// Write the marker that initiates a bundle for the triple \a TargetTriple to
171 /// \a OS.
172 virtual Error WriteBundleStart(raw_fd_ostream &OS,
173 StringRef TargetTriple) = 0;
174
175 /// Write the marker that closes a bundle for the triple \a TargetTriple to \a
176 /// OS.
177 virtual Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) = 0;
178
179 /// Write the bundle from \a Input into \a OS.
180 virtual Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) = 0;
181
182 /// List bundle IDs in \a Input.
listBundleIDs(MemoryBuffer & Input)183 virtual Error listBundleIDs(MemoryBuffer &Input) {
184 if (Error Err = ReadHeader(Input))
185 return Err;
186
187 return forEachBundle(Input, [&](const BundleInfo &Info) -> Error {
188 llvm::outs() << Info.BundleID << '\n';
189 Error Err = listBundleIDsCallback(Input, Info);
190 if (Err)
191 return Err;
192 return Error::success();
193 });
194 }
195
196 /// For each bundle in \a Input, do \a Func.
forEachBundle(MemoryBuffer & Input,std::function<Error (const BundleInfo &)> Func)197 Error forEachBundle(MemoryBuffer &Input,
198 std::function<Error(const BundleInfo &)> Func) {
199 while (true) {
200 Expected<Optional<StringRef>> CurTripleOrErr = ReadBundleStart(Input);
201 if (!CurTripleOrErr)
202 return CurTripleOrErr.takeError();
203
204 // No more bundles.
205 if (!*CurTripleOrErr)
206 break;
207
208 StringRef CurTriple = **CurTripleOrErr;
209 assert(!CurTriple.empty());
210
211 BundleInfo Info{CurTriple};
212 if (Error Err = Func(Info))
213 return Err;
214 }
215 return Error::success();
216 }
217
218 protected:
listBundleIDsCallback(MemoryBuffer & Input,const BundleInfo & Info)219 virtual Error listBundleIDsCallback(MemoryBuffer &Input,
220 const BundleInfo &Info) {
221 return Error::success();
222 }
223 };
224
225 /// Handler for binary files. The bundled file will have the following format
226 /// (all integers are stored in little-endian format):
227 ///
228 /// "OFFLOAD_BUNDLER_MAGIC_STR" (ASCII encoding of the string)
229 ///
230 /// NumberOfOffloadBundles (8-byte integer)
231 ///
232 /// OffsetOfBundle1 (8-byte integer)
233 /// SizeOfBundle1 (8-byte integer)
234 /// NumberOfBytesInTripleOfBundle1 (8-byte integer)
235 /// TripleOfBundle1 (byte length defined before)
236 ///
237 /// ...
238 ///
239 /// OffsetOfBundleN (8-byte integer)
240 /// SizeOfBundleN (8-byte integer)
241 /// NumberOfBytesInTripleOfBundleN (8-byte integer)
242 /// TripleOfBundleN (byte length defined before)
243 ///
244 /// Bundle1
245 /// ...
246 /// BundleN
247
248 /// Read 8-byte integers from a buffer in little-endian format.
Read8byteIntegerFromBuffer(StringRef Buffer,size_t pos)249 static uint64_t Read8byteIntegerFromBuffer(StringRef Buffer, size_t pos) {
250 uint64_t Res = 0;
251 const char *Data = Buffer.data();
252
253 for (unsigned i = 0; i < 8; ++i) {
254 Res <<= 8;
255 uint64_t Char = (uint64_t)Data[pos + 7 - i];
256 Res |= 0xffu & Char;
257 }
258 return Res;
259 }
260
261 /// Write 8-byte integers to a buffer in little-endian format.
Write8byteIntegerToBuffer(raw_fd_ostream & OS,uint64_t Val)262 static void Write8byteIntegerToBuffer(raw_fd_ostream &OS, uint64_t Val) {
263 for (unsigned i = 0; i < 8; ++i) {
264 char Char = (char)(Val & 0xffu);
265 OS.write(&Char, 1);
266 Val >>= 8;
267 }
268 }
269
270 class BinaryFileHandler final : public FileHandler {
271 /// Information about the bundles extracted from the header.
272 struct BinaryBundleInfo final : public BundleInfo {
273 /// Size of the bundle.
274 uint64_t Size = 0u;
275 /// Offset at which the bundle starts in the bundled file.
276 uint64_t Offset = 0u;
277
BinaryBundleInfoBinaryFileHandler::BinaryBundleInfo278 BinaryBundleInfo() {}
BinaryBundleInfoBinaryFileHandler::BinaryBundleInfo279 BinaryBundleInfo(uint64_t Size, uint64_t Offset)
280 : Size(Size), Offset(Offset) {}
281 };
282
283 /// Map between a triple and the corresponding bundle information.
284 StringMap<BinaryBundleInfo> BundlesInfo;
285
286 /// Iterator for the bundle information that is being read.
287 StringMap<BinaryBundleInfo>::iterator CurBundleInfo;
288 StringMap<BinaryBundleInfo>::iterator NextBundleInfo;
289
290 /// Current bundle target to be written.
291 std::string CurWriteBundleTarget;
292
293 public:
BinaryFileHandler()294 BinaryFileHandler() : FileHandler() {}
295
~BinaryFileHandler()296 ~BinaryFileHandler() final {}
297
ReadHeader(MemoryBuffer & Input)298 Error ReadHeader(MemoryBuffer &Input) final {
299 StringRef FC = Input.getBuffer();
300
301 // Initialize the current bundle with the end of the container.
302 CurBundleInfo = BundlesInfo.end();
303
304 // Check if buffer is smaller than magic string.
305 size_t ReadChars = sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
306 if (ReadChars > FC.size())
307 return Error::success();
308
309 // Check if no magic was found.
310 StringRef Magic(FC.data(), sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
311 if (!Magic.equals(OFFLOAD_BUNDLER_MAGIC_STR))
312 return Error::success();
313
314 // Read number of bundles.
315 if (ReadChars + 8 > FC.size())
316 return Error::success();
317
318 uint64_t NumberOfBundles = Read8byteIntegerFromBuffer(FC, ReadChars);
319 ReadChars += 8;
320
321 // Read bundle offsets, sizes and triples.
322 for (uint64_t i = 0; i < NumberOfBundles; ++i) {
323
324 // Read offset.
325 if (ReadChars + 8 > FC.size())
326 return Error::success();
327
328 uint64_t Offset = Read8byteIntegerFromBuffer(FC, ReadChars);
329 ReadChars += 8;
330
331 // Read size.
332 if (ReadChars + 8 > FC.size())
333 return Error::success();
334
335 uint64_t Size = Read8byteIntegerFromBuffer(FC, ReadChars);
336 ReadChars += 8;
337
338 // Read triple size.
339 if (ReadChars + 8 > FC.size())
340 return Error::success();
341
342 uint64_t TripleSize = Read8byteIntegerFromBuffer(FC, ReadChars);
343 ReadChars += 8;
344
345 // Read triple.
346 if (ReadChars + TripleSize > FC.size())
347 return Error::success();
348
349 StringRef Triple(&FC.data()[ReadChars], TripleSize);
350 ReadChars += TripleSize;
351
352 // Check if the offset and size make sense.
353 if (!Offset || Offset + Size > FC.size())
354 return Error::success();
355
356 assert(BundlesInfo.find(Triple) == BundlesInfo.end() &&
357 "Triple is duplicated??");
358 BundlesInfo[Triple] = BinaryBundleInfo(Size, Offset);
359 }
360 // Set the iterator to where we will start to read.
361 CurBundleInfo = BundlesInfo.end();
362 NextBundleInfo = BundlesInfo.begin();
363 return Error::success();
364 }
365
ReadBundleStart(MemoryBuffer & Input)366 Expected<Optional<StringRef>> ReadBundleStart(MemoryBuffer &Input) final {
367 if (NextBundleInfo == BundlesInfo.end())
368 return None;
369 CurBundleInfo = NextBundleInfo++;
370 return CurBundleInfo->first();
371 }
372
ReadBundleEnd(MemoryBuffer & Input)373 Error ReadBundleEnd(MemoryBuffer &Input) final {
374 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
375 return Error::success();
376 }
377
ReadBundle(raw_fd_ostream & OS,MemoryBuffer & Input)378 Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
379 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!");
380 StringRef FC = Input.getBuffer();
381 OS.write(FC.data() + CurBundleInfo->second.Offset,
382 CurBundleInfo->second.Size);
383 return Error::success();
384 }
385
WriteHeader(raw_fd_ostream & OS,ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs)386 Error WriteHeader(raw_fd_ostream &OS,
387 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
388 // Compute size of the header.
389 uint64_t HeaderSize = 0;
390
391 HeaderSize += sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1;
392 HeaderSize += 8; // Number of Bundles
393
394 for (auto &T : TargetNames) {
395 HeaderSize += 3 * 8; // Bundle offset, Size of bundle and size of triple.
396 HeaderSize += T.size(); // The triple.
397 }
398
399 // Write to the buffer the header.
400 OS << OFFLOAD_BUNDLER_MAGIC_STR;
401
402 Write8byteIntegerToBuffer(OS, TargetNames.size());
403
404 unsigned Idx = 0;
405 for (auto &T : TargetNames) {
406 MemoryBuffer &MB = *Inputs[Idx++];
407 HeaderSize = alignTo(HeaderSize, BundleAlignment);
408 // Bundle offset.
409 Write8byteIntegerToBuffer(OS, HeaderSize);
410 // Size of the bundle (adds to the next bundle's offset)
411 Write8byteIntegerToBuffer(OS, MB.getBufferSize());
412 BundlesInfo[T] = BinaryBundleInfo(MB.getBufferSize(), HeaderSize);
413 HeaderSize += MB.getBufferSize();
414 // Size of the triple
415 Write8byteIntegerToBuffer(OS, T.size());
416 // Triple
417 OS << T;
418 }
419 return Error::success();
420 }
421
WriteBundleStart(raw_fd_ostream & OS,StringRef TargetTriple)422 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {
423 CurWriteBundleTarget = TargetTriple.str();
424 return Error::success();
425 }
426
WriteBundleEnd(raw_fd_ostream & OS,StringRef TargetTriple)427 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {
428 return Error::success();
429 }
430
WriteBundle(raw_fd_ostream & OS,MemoryBuffer & Input)431 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
432 auto BI = BundlesInfo[CurWriteBundleTarget];
433 OS.seek(BI.Offset);
434 OS.write(Input.getBufferStart(), Input.getBufferSize());
435 return Error::success();
436 }
437 };
438
439 namespace {
440
441 // This class implements a list of temporary files that are removed upon
442 // object destruction.
443 class TempFileHandlerRAII {
444 public:
~TempFileHandlerRAII()445 ~TempFileHandlerRAII() {
446 for (const auto &File : Files)
447 sys::fs::remove(File);
448 }
449
450 // Creates temporary file with given contents.
Create(Optional<ArrayRef<char>> Contents)451 Expected<StringRef> Create(Optional<ArrayRef<char>> Contents) {
452 SmallString<128u> File;
453 if (std::error_code EC =
454 sys::fs::createTemporaryFile("clang-offload-bundler", "tmp", File))
455 return createFileError(File, EC);
456 Files.push_front(File);
457
458 if (Contents) {
459 std::error_code EC;
460 raw_fd_ostream OS(File, EC);
461 if (EC)
462 return createFileError(File, EC);
463 OS.write(Contents->data(), Contents->size());
464 }
465 return Files.front();
466 }
467
468 private:
469 std::forward_list<SmallString<128u>> Files;
470 };
471
472 } // end anonymous namespace
473
474 /// Handler for object files. The bundles are organized by sections with a
475 /// designated name.
476 ///
477 /// To unbundle, we just copy the contents of the designated section.
478 class ObjectFileHandler final : public FileHandler {
479
480 /// The object file we are currently dealing with.
481 std::unique_ptr<ObjectFile> Obj;
482
483 /// Return the input file contents.
getInputFileContents() const484 StringRef getInputFileContents() const { return Obj->getData(); }
485
486 /// Return bundle name (<kind>-<triple>) if the provided section is an offload
487 /// section.
IsOffloadSection(SectionRef CurSection)488 static Expected<Optional<StringRef>> IsOffloadSection(SectionRef CurSection) {
489 Expected<StringRef> NameOrErr = CurSection.getName();
490 if (!NameOrErr)
491 return NameOrErr.takeError();
492
493 // If it does not start with the reserved suffix, just skip this section.
494 if (!NameOrErr->startswith(OFFLOAD_BUNDLER_MAGIC_STR))
495 return None;
496
497 // Return the triple that is right after the reserved prefix.
498 return NameOrErr->substr(sizeof(OFFLOAD_BUNDLER_MAGIC_STR) - 1);
499 }
500
501 /// Total number of inputs.
502 unsigned NumberOfInputs = 0;
503
504 /// Total number of processed inputs, i.e, inputs that were already
505 /// read from the buffers.
506 unsigned NumberOfProcessedInputs = 0;
507
508 /// Iterator of the current and next section.
509 section_iterator CurrentSection;
510 section_iterator NextSection;
511
512 public:
ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn)513 ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn)
514 : FileHandler(), Obj(std::move(ObjIn)),
515 CurrentSection(Obj->section_begin()),
516 NextSection(Obj->section_begin()) {}
517
~ObjectFileHandler()518 ~ObjectFileHandler() final {}
519
ReadHeader(MemoryBuffer & Input)520 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
521
ReadBundleStart(MemoryBuffer & Input)522 Expected<Optional<StringRef>> ReadBundleStart(MemoryBuffer &Input) final {
523 while (NextSection != Obj->section_end()) {
524 CurrentSection = NextSection;
525 ++NextSection;
526
527 // Check if the current section name starts with the reserved prefix. If
528 // so, return the triple.
529 Expected<Optional<StringRef>> TripleOrErr =
530 IsOffloadSection(*CurrentSection);
531 if (!TripleOrErr)
532 return TripleOrErr.takeError();
533 if (*TripleOrErr)
534 return **TripleOrErr;
535 }
536 return None;
537 }
538
ReadBundleEnd(MemoryBuffer & Input)539 Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); }
540
ReadBundle(raw_fd_ostream & OS,MemoryBuffer & Input)541 Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
542 Expected<StringRef> ContentOrErr = CurrentSection->getContents();
543 if (!ContentOrErr)
544 return ContentOrErr.takeError();
545 StringRef Content = *ContentOrErr;
546
547 // Copy fat object contents to the output when extracting host bundle.
548 if (Content.size() == 1u && Content.front() == 0)
549 Content = StringRef(Input.getBufferStart(), Input.getBufferSize());
550
551 OS.write(Content.data(), Content.size());
552 return Error::success();
553 }
554
WriteHeader(raw_fd_ostream & OS,ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs)555 Error WriteHeader(raw_fd_ostream &OS,
556 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
557 assert(HostInputIndex != ~0u && "Host input index not defined.");
558
559 // Record number of inputs.
560 NumberOfInputs = Inputs.size();
561 return Error::success();
562 }
563
WriteBundleStart(raw_fd_ostream & OS,StringRef TargetTriple)564 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {
565 ++NumberOfProcessedInputs;
566 return Error::success();
567 }
568
WriteBundleEnd(raw_fd_ostream & OS,StringRef TargetTriple)569 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {
570 assert(NumberOfProcessedInputs <= NumberOfInputs &&
571 "Processing more inputs that actually exist!");
572 assert(HostInputIndex != ~0u && "Host input index not defined.");
573
574 // If this is not the last output, we don't have to do anything.
575 if (NumberOfProcessedInputs != NumberOfInputs)
576 return Error::success();
577
578 // We will use llvm-objcopy to add target objects sections to the output
579 // fat object. These sections should have 'exclude' flag set which tells
580 // link editor to remove them from linker inputs when linking executable or
581 // shared library.
582
583 // Find llvm-objcopy in order to create the bundle binary.
584 ErrorOr<std::string> Objcopy = sys::findProgramByName(
585 "llvm-objcopy", sys::path::parent_path(BundlerExecutable));
586 if (!Objcopy)
587 Objcopy = sys::findProgramByName("llvm-objcopy");
588 if (!Objcopy)
589 return createStringError(Objcopy.getError(),
590 "unable to find 'llvm-objcopy' in path");
591
592 // We write to the output file directly. So, we close it and use the name
593 // to pass down to llvm-objcopy.
594 OS.close();
595
596 // Temporary files that need to be removed.
597 TempFileHandlerRAII TempFiles;
598
599 // Compose llvm-objcopy command line for add target objects' sections with
600 // appropriate flags.
601 BumpPtrAllocator Alloc;
602 StringSaver SS{Alloc};
603 SmallVector<StringRef, 8u> ObjcopyArgs{"llvm-objcopy"};
604 for (unsigned I = 0; I < NumberOfInputs; ++I) {
605 StringRef InputFile = InputFileNames[I];
606 if (I == HostInputIndex) {
607 // Special handling for the host bundle. We do not need to add a
608 // standard bundle for the host object since we are going to use fat
609 // object as a host object. Therefore use dummy contents (one zero byte)
610 // when creating section for the host bundle.
611 Expected<StringRef> TempFileOrErr = TempFiles.Create(ArrayRef<char>(0));
612 if (!TempFileOrErr)
613 return TempFileOrErr.takeError();
614 InputFile = *TempFileOrErr;
615 }
616
617 ObjcopyArgs.push_back(SS.save(Twine("--add-section=") +
618 OFFLOAD_BUNDLER_MAGIC_STR + TargetNames[I] +
619 "=" + InputFile));
620 ObjcopyArgs.push_back(SS.save(Twine("--set-section-flags=") +
621 OFFLOAD_BUNDLER_MAGIC_STR + TargetNames[I] +
622 "=readonly,exclude"));
623 }
624 ObjcopyArgs.push_back("--");
625 ObjcopyArgs.push_back(InputFileNames[HostInputIndex]);
626 ObjcopyArgs.push_back(OutputFileNames.front());
627
628 if (Error Err = executeObjcopy(*Objcopy, ObjcopyArgs))
629 return Err;
630
631 return Error::success();
632 }
633
WriteBundle(raw_fd_ostream & OS,MemoryBuffer & Input)634 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
635 return Error::success();
636 }
637
638 private:
executeObjcopy(StringRef Objcopy,ArrayRef<StringRef> Args)639 static Error executeObjcopy(StringRef Objcopy, ArrayRef<StringRef> Args) {
640 // If the user asked for the commands to be printed out, we do that
641 // instead of executing it.
642 if (PrintExternalCommands) {
643 errs() << "\"" << Objcopy << "\"";
644 for (StringRef Arg : drop_begin(Args, 1))
645 errs() << " \"" << Arg << "\"";
646 errs() << "\n";
647 } else {
648 if (sys::ExecuteAndWait(Objcopy, Args))
649 return createStringError(inconvertibleErrorCode(),
650 "'llvm-objcopy' tool failed");
651 }
652 return Error::success();
653 }
654 };
655
656 /// Handler for text files. The bundled file will have the following format.
657 ///
658 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
659 /// Bundle 1
660 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
661 /// ...
662 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__START__ triple"
663 /// Bundle N
664 /// "Comment OFFLOAD_BUNDLER_MAGIC_STR__END__ triple"
665 class TextFileHandler final : public FileHandler {
666 /// String that begins a line comment.
667 StringRef Comment;
668
669 /// String that initiates a bundle.
670 std::string BundleStartString;
671
672 /// String that closes a bundle.
673 std::string BundleEndString;
674
675 /// Number of chars read from input.
676 size_t ReadChars = 0u;
677
678 protected:
ReadHeader(MemoryBuffer & Input)679 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); }
680
ReadBundleStart(MemoryBuffer & Input)681 Expected<Optional<StringRef>> ReadBundleStart(MemoryBuffer &Input) final {
682 StringRef FC = Input.getBuffer();
683
684 // Find start of the bundle.
685 ReadChars = FC.find(BundleStartString, ReadChars);
686 if (ReadChars == FC.npos)
687 return None;
688
689 // Get position of the triple.
690 size_t TripleStart = ReadChars = ReadChars + BundleStartString.size();
691
692 // Get position that closes the triple.
693 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars);
694 if (TripleEnd == FC.npos)
695 return None;
696
697 // Next time we read after the new line.
698 ++ReadChars;
699
700 return StringRef(&FC.data()[TripleStart], TripleEnd - TripleStart);
701 }
702
ReadBundleEnd(MemoryBuffer & Input)703 Error ReadBundleEnd(MemoryBuffer &Input) final {
704 StringRef FC = Input.getBuffer();
705
706 // Read up to the next new line.
707 assert(FC[ReadChars] == '\n' && "The bundle should end with a new line.");
708
709 size_t TripleEnd = ReadChars = FC.find("\n", ReadChars + 1);
710 if (TripleEnd != FC.npos)
711 // Next time we read after the new line.
712 ++ReadChars;
713
714 return Error::success();
715 }
716
ReadBundle(raw_fd_ostream & OS,MemoryBuffer & Input)717 Error ReadBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
718 StringRef FC = Input.getBuffer();
719 size_t BundleStart = ReadChars;
720
721 // Find end of the bundle.
722 size_t BundleEnd = ReadChars = FC.find(BundleEndString, ReadChars);
723
724 StringRef Bundle(&FC.data()[BundleStart], BundleEnd - BundleStart);
725 OS << Bundle;
726
727 return Error::success();
728 }
729
WriteHeader(raw_fd_ostream & OS,ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs)730 Error WriteHeader(raw_fd_ostream &OS,
731 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final {
732 return Error::success();
733 }
734
WriteBundleStart(raw_fd_ostream & OS,StringRef TargetTriple)735 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final {
736 OS << BundleStartString << TargetTriple << "\n";
737 return Error::success();
738 }
739
WriteBundleEnd(raw_fd_ostream & OS,StringRef TargetTriple)740 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final {
741 OS << BundleEndString << TargetTriple << "\n";
742 return Error::success();
743 }
744
WriteBundle(raw_fd_ostream & OS,MemoryBuffer & Input)745 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final {
746 OS << Input.getBuffer();
747 return Error::success();
748 }
749
750 public:
TextFileHandler(StringRef Comment)751 TextFileHandler(StringRef Comment)
752 : FileHandler(), Comment(Comment), ReadChars(0) {
753 BundleStartString =
754 "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__START__ ";
755 BundleEndString =
756 "\n" + Comment.str() + " " OFFLOAD_BUNDLER_MAGIC_STR "__END__ ";
757 }
758
listBundleIDsCallback(MemoryBuffer & Input,const BundleInfo & Info)759 Error listBundleIDsCallback(MemoryBuffer &Input,
760 const BundleInfo &Info) final {
761 // TODO: To list bundle IDs in a bundled text file we need to go through
762 // all bundles. The format of bundled text file may need to include a
763 // header if the performance of listing bundle IDs of bundled text file is
764 // important.
765 ReadChars = Input.getBuffer().find(BundleEndString, ReadChars);
766 if (Error Err = ReadBundleEnd(Input))
767 return Err;
768 return Error::success();
769 }
770 };
771
772 /// Return an appropriate object file handler. We use the specific object
773 /// handler if we know how to deal with that format, otherwise we use a default
774 /// binary file handler.
775 static std::unique_ptr<FileHandler>
CreateObjectFileHandler(MemoryBuffer & FirstInput)776 CreateObjectFileHandler(MemoryBuffer &FirstInput) {
777 // Check if the input file format is one that we know how to deal with.
778 Expected<std::unique_ptr<Binary>> BinaryOrErr = createBinary(FirstInput);
779
780 // We only support regular object files. If failed to open the input as a
781 // known binary or this is not an object file use the default binary handler.
782 if (errorToBool(BinaryOrErr.takeError()) || !isa<ObjectFile>(*BinaryOrErr))
783 return std::make_unique<BinaryFileHandler>();
784
785 // Otherwise create an object file handler. The handler will be owned by the
786 // client of this function.
787 return std::make_unique<ObjectFileHandler>(
788 std::unique_ptr<ObjectFile>(cast<ObjectFile>(BinaryOrErr->release())));
789 }
790
791 /// Return an appropriate handler given the input files and options.
792 static Expected<std::unique_ptr<FileHandler>>
CreateFileHandler(MemoryBuffer & FirstInput)793 CreateFileHandler(MemoryBuffer &FirstInput) {
794 if (FilesType == "i")
795 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
796 if (FilesType == "ii")
797 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
798 if (FilesType == "cui")
799 return std::make_unique<TextFileHandler>(/*Comment=*/"//");
800 // TODO: `.d` should be eventually removed once `-M` and its variants are
801 // handled properly in offload compilation.
802 if (FilesType == "d")
803 return std::make_unique<TextFileHandler>(/*Comment=*/"#");
804 if (FilesType == "ll")
805 return std::make_unique<TextFileHandler>(/*Comment=*/";");
806 if (FilesType == "bc")
807 return std::make_unique<BinaryFileHandler>();
808 if (FilesType == "s")
809 return std::make_unique<TextFileHandler>(/*Comment=*/"#");
810 if (FilesType == "o")
811 return CreateObjectFileHandler(FirstInput);
812 if (FilesType == "gch")
813 return std::make_unique<BinaryFileHandler>();
814 if (FilesType == "ast")
815 return std::make_unique<BinaryFileHandler>();
816
817 return createStringError(errc::invalid_argument,
818 "'" + FilesType + "': invalid file type specified");
819 }
820
821 /// Bundle the files. Return true if an error was found.
BundleFiles()822 static Error BundleFiles() {
823 std::error_code EC;
824
825 // Create output file.
826 raw_fd_ostream OutputFile(OutputFileNames.front(), EC, sys::fs::OF_None);
827 if (EC)
828 return createFileError(OutputFileNames.front(), EC);
829
830 // Open input files.
831 SmallVector<std::unique_ptr<MemoryBuffer>, 8u> InputBuffers;
832 InputBuffers.reserve(InputFileNames.size());
833 for (auto &I : InputFileNames) {
834 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
835 MemoryBuffer::getFileOrSTDIN(I);
836 if (std::error_code EC = CodeOrErr.getError())
837 return createFileError(I, EC);
838 InputBuffers.emplace_back(std::move(*CodeOrErr));
839 }
840
841 // Get the file handler. We use the host buffer as reference.
842 assert(HostInputIndex != ~0u && "Host input index undefined??");
843 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
844 CreateFileHandler(*InputBuffers[HostInputIndex]);
845 if (!FileHandlerOrErr)
846 return FileHandlerOrErr.takeError();
847
848 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
849 assert(FH);
850
851 // Write header.
852 if (Error Err = FH->WriteHeader(OutputFile, InputBuffers))
853 return Err;
854
855 // Write all bundles along with the start/end markers. If an error was found
856 // writing the end of the bundle component, abort the bundle writing.
857 auto Input = InputBuffers.begin();
858 for (auto &Triple : TargetNames) {
859 if (Error Err = FH->WriteBundleStart(OutputFile, Triple))
860 return Err;
861 if (Error Err = FH->WriteBundle(OutputFile, **Input))
862 return Err;
863 if (Error Err = FH->WriteBundleEnd(OutputFile, Triple))
864 return Err;
865 ++Input;
866 }
867 return Error::success();
868 }
869
870 // List bundle IDs. Return true if an error was found.
ListBundleIDsInFile(StringRef InputFileName)871 static Error ListBundleIDsInFile(StringRef InputFileName) {
872 // Open Input file.
873 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
874 MemoryBuffer::getFileOrSTDIN(InputFileName);
875 if (std::error_code EC = CodeOrErr.getError())
876 return createFileError(InputFileName, EC);
877
878 MemoryBuffer &Input = **CodeOrErr;
879
880 // Select the right files handler.
881 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
882 CreateFileHandler(Input);
883 if (!FileHandlerOrErr)
884 return FileHandlerOrErr.takeError();
885
886 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
887 assert(FH);
888 return FH->listBundleIDs(Input);
889 }
890
891 // Unbundle the files. Return true if an error was found.
UnbundleFiles()892 static Error UnbundleFiles() {
893 // Open Input file.
894 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
895 MemoryBuffer::getFileOrSTDIN(InputFileNames.front());
896 if (std::error_code EC = CodeOrErr.getError())
897 return createFileError(InputFileNames.front(), EC);
898
899 MemoryBuffer &Input = **CodeOrErr;
900
901 // Select the right files handler.
902 Expected<std::unique_ptr<FileHandler>> FileHandlerOrErr =
903 CreateFileHandler(Input);
904 if (!FileHandlerOrErr)
905 return FileHandlerOrErr.takeError();
906
907 std::unique_ptr<FileHandler> &FH = *FileHandlerOrErr;
908 assert(FH);
909
910 // Read the header of the bundled file.
911 if (Error Err = FH->ReadHeader(Input))
912 return Err;
913
914 // Create a work list that consist of the map triple/output file.
915 StringMap<StringRef> Worklist;
916 auto Output = OutputFileNames.begin();
917 for (auto &Triple : TargetNames) {
918 Worklist[Triple] = *Output;
919 ++Output;
920 }
921
922 // Read all the bundles that are in the work list. If we find no bundles we
923 // assume the file is meant for the host target.
924 bool FoundHostBundle = false;
925 while (!Worklist.empty()) {
926 Expected<Optional<StringRef>> CurTripleOrErr = FH->ReadBundleStart(Input);
927 if (!CurTripleOrErr)
928 return CurTripleOrErr.takeError();
929
930 // We don't have more bundles.
931 if (!*CurTripleOrErr)
932 break;
933
934 StringRef CurTriple = **CurTripleOrErr;
935 assert(!CurTriple.empty());
936
937 auto Output = Worklist.find(CurTriple);
938 // The file may have more bundles for other targets, that we don't care
939 // about. Therefore, move on to the next triple
940 if (Output == Worklist.end())
941 continue;
942
943 // Check if the output file can be opened and copy the bundle to it.
944 std::error_code EC;
945 raw_fd_ostream OutputFile(Output->second, EC, sys::fs::OF_None);
946 if (EC)
947 return createFileError(Output->second, EC);
948 if (Error Err = FH->ReadBundle(OutputFile, Input))
949 return Err;
950 if (Error Err = FH->ReadBundleEnd(Input))
951 return Err;
952 Worklist.erase(Output);
953
954 // Record if we found the host bundle.
955 if (hasHostKind(CurTriple))
956 FoundHostBundle = true;
957 }
958
959 if (!AllowMissingBundles && !Worklist.empty()) {
960 std::string ErrMsg = "Can't find bundles for";
961 std::set<StringRef> Sorted;
962 for (auto &E : Worklist)
963 Sorted.insert(E.first());
964 unsigned I = 0;
965 unsigned Last = Sorted.size() - 1;
966 for (auto &E : Sorted) {
967 if (I != 0 && Last > 1)
968 ErrMsg += ",";
969 ErrMsg += " ";
970 if (I == Last && I != 0)
971 ErrMsg += "and ";
972 ErrMsg += E.str();
973 ++I;
974 }
975 return createStringError(inconvertibleErrorCode(), ErrMsg);
976 }
977
978 // If no bundles were found, assume the input file is the host bundle and
979 // create empty files for the remaining targets.
980 if (Worklist.size() == TargetNames.size()) {
981 for (auto &E : Worklist) {
982 std::error_code EC;
983 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
984 if (EC)
985 return createFileError(E.second, EC);
986
987 // If this entry has a host kind, copy the input file to the output file.
988 if (hasHostKind(E.first()))
989 OutputFile.write(Input.getBufferStart(), Input.getBufferSize());
990 }
991 return Error::success();
992 }
993
994 // If we found elements, we emit an error if none of those were for the host
995 // in case host bundle name was provided in command line.
996 if (!FoundHostBundle && HostInputIndex != ~0u)
997 return createStringError(inconvertibleErrorCode(),
998 "Can't find bundle for the host target");
999
1000 // If we still have any elements in the worklist, create empty files for them.
1001 for (auto &E : Worklist) {
1002 std::error_code EC;
1003 raw_fd_ostream OutputFile(E.second, EC, sys::fs::OF_None);
1004 if (EC)
1005 return createFileError(E.second, EC);
1006 }
1007
1008 return Error::success();
1009 }
1010
PrintVersion(raw_ostream & OS)1011 static void PrintVersion(raw_ostream &OS) {
1012 OS << clang::getClangToolFullVersion("clang-offload-bundler") << '\n';
1013 }
1014
main(int argc,const char ** argv)1015 int main(int argc, const char **argv) {
1016 sys::PrintStackTraceOnErrorSignal(argv[0]);
1017
1018 cl::HideUnrelatedOptions(ClangOffloadBundlerCategory);
1019 cl::SetVersionPrinter(PrintVersion);
1020 cl::ParseCommandLineOptions(
1021 argc, argv,
1022 "A tool to bundle several input files of the specified type <type> \n"
1023 "referring to the same source file but different targets into a single \n"
1024 "one. The resulting file can also be unbundled into different files by \n"
1025 "this tool if -unbundle is provided.\n");
1026
1027 if (Help) {
1028 cl::PrintHelpMessage();
1029 return 0;
1030 }
1031
1032 auto reportError = [argv](Error E) {
1033 logAllUnhandledErrors(std::move(E), WithColor::error(errs(), argv[0]));
1034 exit(1);
1035 };
1036
1037 auto doWork = [&](std::function<llvm::Error()> Work) {
1038 // Save the current executable directory as it will be useful to find other
1039 // tools.
1040 BundlerExecutable = argv[0];
1041 if (!llvm::sys::fs::exists(BundlerExecutable))
1042 BundlerExecutable =
1043 sys::fs::getMainExecutable(argv[0], &BundlerExecutable);
1044
1045 if (llvm::Error Err = Work()) {
1046 reportError(std::move(Err));
1047 }
1048 };
1049
1050 if (ListBundleIDs) {
1051 if (Unbundle) {
1052 reportError(
1053 createStringError(errc::invalid_argument,
1054 "-unbundle and -list cannot be used together"));
1055 }
1056 if (InputFileNames.size() != 1) {
1057 reportError(createStringError(errc::invalid_argument,
1058 "only one input file supported for -list"));
1059 }
1060 if (OutputFileNames.size()) {
1061 reportError(createStringError(errc::invalid_argument,
1062 "-outputs option is invalid for -list"));
1063 }
1064 if (TargetNames.size()) {
1065 reportError(createStringError(errc::invalid_argument,
1066 "-targets option is invalid for -list"));
1067 }
1068
1069 doWork([]() { return ListBundleIDsInFile(InputFileNames.front()); });
1070 return 0;
1071 }
1072
1073 if (OutputFileNames.getNumOccurrences() == 0) {
1074 reportError(createStringError(
1075 errc::invalid_argument,
1076 "for the --outputs option: must be specified at least once!"));
1077 }
1078 if (TargetNames.getNumOccurrences() == 0) {
1079 reportError(createStringError(
1080 errc::invalid_argument,
1081 "for the --targets option: must be specified at least once!"));
1082 }
1083 if (Unbundle) {
1084 if (InputFileNames.size() != 1) {
1085 reportError(createStringError(
1086 errc::invalid_argument,
1087 "only one input file supported in unbundling mode"));
1088 }
1089 if (OutputFileNames.size() != TargetNames.size()) {
1090 reportError(createStringError(errc::invalid_argument,
1091 "number of output files and targets should "
1092 "match in unbundling mode"));
1093 }
1094 } else {
1095 if (OutputFileNames.size() != 1) {
1096 reportError(createStringError(
1097 errc::invalid_argument,
1098 "only one output file supported in bundling mode"));
1099 }
1100 if (InputFileNames.size() != TargetNames.size()) {
1101 reportError(createStringError(
1102 errc::invalid_argument,
1103 "number of input files and targets should match in bundling mode"));
1104 }
1105 }
1106
1107 // Verify that the offload kinds and triples are known. We also check that we
1108 // have exactly one host target.
1109 unsigned Index = 0u;
1110 unsigned HostTargetNum = 0u;
1111 llvm::DenseSet<StringRef> ParsedTargets;
1112 for (StringRef Target : TargetNames) {
1113 if (ParsedTargets.contains(Target)) {
1114 reportError(createStringError(errc::invalid_argument,
1115 "Duplicate targets are not allowed"));
1116 }
1117 ParsedTargets.insert(Target);
1118
1119 StringRef Kind;
1120 StringRef Triple;
1121 getOffloadKindAndTriple(Target, Kind, Triple);
1122
1123 bool KindIsValid = !Kind.empty();
1124 KindIsValid = KindIsValid && StringSwitch<bool>(Kind)
1125 .Case("host", true)
1126 .Case("openmp", true)
1127 .Case("hip", true)
1128 .Case("hipv4", true)
1129 .Default(false);
1130
1131 bool TripleIsValid = !Triple.empty();
1132 llvm::Triple T(Triple);
1133 TripleIsValid &= T.getArch() != Triple::UnknownArch;
1134
1135 if (!KindIsValid || !TripleIsValid) {
1136 SmallVector<char, 128u> Buf;
1137 raw_svector_ostream Msg(Buf);
1138 Msg << "invalid target '" << Target << "'";
1139 if (!KindIsValid)
1140 Msg << ", unknown offloading kind '" << Kind << "'";
1141 if (!TripleIsValid)
1142 Msg << ", unknown target triple '" << Triple << "'";
1143 reportError(createStringError(errc::invalid_argument, Msg.str()));
1144 }
1145
1146 if (KindIsValid && Kind == "host") {
1147 ++HostTargetNum;
1148 // Save the index of the input that refers to the host.
1149 HostInputIndex = Index;
1150 }
1151
1152 ++Index;
1153 }
1154
1155 // Host triple is not really needed for unbundling operation, so do not
1156 // treat missing host triple as error if we do unbundling.
1157 if ((Unbundle && HostTargetNum > 1) || (!Unbundle && HostTargetNum != 1)) {
1158 reportError(createStringError(errc::invalid_argument,
1159 "expecting exactly one host target but got " +
1160 Twine(HostTargetNum)));
1161 }
1162
1163 doWork([]() { return Unbundle ? UnbundleFiles() : BundleFiles(); });
1164 return 0;
1165 }
1166