xref: /netbsd-src/external/apache2/llvm/dist/clang/tools/clang-offload-bundler/ClangOffloadBundler.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
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