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