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. 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 } 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 145 FileHandler() {} 146 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. 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. 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: 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. 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. 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 278 BinaryBundleInfo() {} 279 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: 294 BinaryFileHandler() : FileHandler() {} 295 296 ~BinaryFileHandler() final {} 297 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 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 373 Error ReadBundleEnd(MemoryBuffer &Input) final { 374 assert(CurBundleInfo != BundlesInfo.end() && "Invalid reader info!"); 375 return Error::success(); 376 } 377 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 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 422 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { 423 CurWriteBundleTarget = TargetTriple.str(); 424 return Error::success(); 425 } 426 427 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { 428 return Error::success(); 429 } 430 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: 445 ~TempFileHandlerRAII() { 446 for (const auto &File : Files) 447 sys::fs::remove(File); 448 } 449 450 // Creates temporary file with given 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. 484 StringRef getInputFileContents() const { return Obj->getData(); } 485 486 /// Return bundle name (<kind>-<triple>) if the provided section is an offload 487 /// section. 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: 513 ObjectFileHandler(std::unique_ptr<ObjectFile> ObjIn) 514 : FileHandler(), Obj(std::move(ObjIn)), 515 CurrentSection(Obj->section_begin()), 516 NextSection(Obj->section_begin()) {} 517 518 ~ObjectFileHandler() final {} 519 520 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 521 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 539 Error ReadBundleEnd(MemoryBuffer &Input) final { return Error::success(); } 540 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 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 564 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { 565 ++NumberOfProcessedInputs; 566 return Error::success(); 567 } 568 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 634 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { 635 return Error::success(); 636 } 637 638 private: 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: 679 Error ReadHeader(MemoryBuffer &Input) final { return Error::success(); } 680 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 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 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 730 Error WriteHeader(raw_fd_ostream &OS, 731 ArrayRef<std::unique_ptr<MemoryBuffer>> Inputs) final { 732 return Error::success(); 733 } 734 735 Error WriteBundleStart(raw_fd_ostream &OS, StringRef TargetTriple) final { 736 OS << BundleStartString << TargetTriple << "\n"; 737 return Error::success(); 738 } 739 740 Error WriteBundleEnd(raw_fd_ostream &OS, StringRef TargetTriple) final { 741 OS << BundleEndString << TargetTriple << "\n"; 742 return Error::success(); 743 } 744 745 Error WriteBundle(raw_fd_ostream &OS, MemoryBuffer &Input) final { 746 OS << Input.getBuffer(); 747 return Error::success(); 748 } 749 750 public: 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 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> 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>> 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. 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. 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. 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 1011 static void PrintVersion(raw_ostream &OS) { 1012 OS << clang::getClangToolFullVersion("clang-offload-bundler") << '\n'; 1013 } 1014 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