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