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