1 //===- dsymutil.cpp - Debug info dumping utility for llvm -----------------===// 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 // This program is a utility that aims to be a dropin replacement for Darwin's 10 // dsymutil. 11 //===----------------------------------------------------------------------===// 12 13 #include "dsymutil.h" 14 #include "BinaryHolder.h" 15 #include "CFBundle.h" 16 #include "DebugMap.h" 17 #include "LinkUtils.h" 18 #include "MachOUtils.h" 19 #include "llvm/ADT/SmallString.h" 20 #include "llvm/ADT/SmallVector.h" 21 #include "llvm/ADT/StringExtras.h" 22 #include "llvm/ADT/StringRef.h" 23 #include "llvm/ADT/StringSwitch.h" 24 #include "llvm/ADT/Triple.h" 25 #include "llvm/DebugInfo/DIContext.h" 26 #include "llvm/DebugInfo/DWARF/DWARFContext.h" 27 #include "llvm/DebugInfo/DWARF/DWARFVerifier.h" 28 #include "llvm/Object/Binary.h" 29 #include "llvm/Object/MachO.h" 30 #include "llvm/Option/Arg.h" 31 #include "llvm/Option/ArgList.h" 32 #include "llvm/Option/Option.h" 33 #include "llvm/Support/CommandLine.h" 34 #include "llvm/Support/FileSystem.h" 35 #include "llvm/Support/InitLLVM.h" 36 #include "llvm/Support/ManagedStatic.h" 37 #include "llvm/Support/Path.h" 38 #include "llvm/Support/TargetSelect.h" 39 #include "llvm/Support/ThreadPool.h" 40 #include "llvm/Support/WithColor.h" 41 #include "llvm/Support/raw_ostream.h" 42 #include "llvm/Support/thread.h" 43 #include <algorithm> 44 #include <cstdint> 45 #include <cstdlib> 46 #include <string> 47 #include <system_error> 48 49 using namespace llvm; 50 using namespace llvm::dsymutil; 51 using namespace object; 52 53 namespace { 54 enum ID { 55 OPT_INVALID = 0, // This is not an option ID. 56 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ 57 HELPTEXT, METAVAR, VALUES) \ 58 OPT_##ID, 59 #include "Options.inc" 60 #undef OPTION 61 }; 62 63 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; 64 #include "Options.inc" 65 #undef PREFIX 66 67 const opt::OptTable::Info InfoTable[] = { 68 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ 69 HELPTEXT, METAVAR, VALUES) \ 70 { \ 71 PREFIX, NAME, HELPTEXT, \ 72 METAVAR, OPT_##ID, opt::Option::KIND##Class, \ 73 PARAM, FLAGS, OPT_##GROUP, \ 74 OPT_##ALIAS, ALIASARGS, VALUES}, 75 #include "Options.inc" 76 #undef OPTION 77 }; 78 79 class DsymutilOptTable : public opt::OptTable { 80 public: 81 DsymutilOptTable() : OptTable(InfoTable) {} 82 }; 83 } // namespace 84 85 struct DsymutilOptions { 86 bool DumpDebugMap = false; 87 bool DumpStab = false; 88 bool Flat = false; 89 bool InputIsYAMLDebugMap = false; 90 bool PaperTrailWarnings = false; 91 bool Verify = false; 92 std::string SymbolMap; 93 std::string OutputFile; 94 std::string Toolchain; 95 std::vector<std::string> Archs; 96 std::vector<std::string> InputFiles; 97 unsigned NumThreads; 98 dsymutil::LinkOptions LinkOpts; 99 }; 100 101 /// Return a list of input files. This function has logic for dealing with the 102 /// special case where we might have dSYM bundles as input. The function 103 /// returns an error when the directory structure doesn't match that of a dSYM 104 /// bundle. 105 static Expected<std::vector<std::string>> getInputs(opt::InputArgList &Args, 106 bool DsymAsInput) { 107 std::vector<std::string> InputFiles; 108 for (auto *File : Args.filtered(OPT_INPUT)) 109 InputFiles.push_back(File->getValue()); 110 111 if (!DsymAsInput) 112 return InputFiles; 113 114 // If we are updating, we might get dSYM bundles as input. 115 std::vector<std::string> Inputs; 116 for (const auto &Input : InputFiles) { 117 if (!sys::fs::is_directory(Input)) { 118 Inputs.push_back(Input); 119 continue; 120 } 121 122 // Make sure that we're dealing with a dSYM bundle. 123 SmallString<256> BundlePath(Input); 124 sys::path::append(BundlePath, "Contents", "Resources", "DWARF"); 125 if (!sys::fs::is_directory(BundlePath)) 126 return make_error<StringError>( 127 Input + " is a directory, but doesn't look like a dSYM bundle.", 128 inconvertibleErrorCode()); 129 130 // Create a directory iterator to iterate over all the entries in the 131 // bundle. 132 std::error_code EC; 133 sys::fs::directory_iterator DirIt(BundlePath, EC); 134 sys::fs::directory_iterator DirEnd; 135 if (EC) 136 return errorCodeToError(EC); 137 138 // Add each entry to the list of inputs. 139 while (DirIt != DirEnd) { 140 Inputs.push_back(DirIt->path()); 141 DirIt.increment(EC); 142 if (EC) 143 return errorCodeToError(EC); 144 } 145 } 146 return Inputs; 147 } 148 149 // Verify that the given combination of options makes sense. 150 static Error verifyOptions(const DsymutilOptions &Options) { 151 if (Options.InputFiles.empty()) { 152 return make_error<StringError>("no input files specified", 153 errc::invalid_argument); 154 } 155 156 if (Options.LinkOpts.Update && 157 std::find(Options.InputFiles.begin(), Options.InputFiles.end(), "-") != 158 Options.InputFiles.end()) { 159 // FIXME: We cannot use stdin for an update because stdin will be 160 // consumed by the BinaryHolder during the debugmap parsing, and 161 // then we will want to consume it again in DwarfLinker. If we 162 // used a unique BinaryHolder object that could cache multiple 163 // binaries this restriction would go away. 164 return make_error<StringError>( 165 "standard input cannot be used as input for a dSYM update.", 166 errc::invalid_argument); 167 } 168 169 if (!Options.Flat && Options.OutputFile == "-") 170 return make_error<StringError>( 171 "cannot emit to standard output without --flat.", 172 errc::invalid_argument); 173 174 if (Options.InputFiles.size() > 1 && Options.Flat && 175 !Options.OutputFile.empty()) 176 return make_error<StringError>( 177 "cannot use -o with multiple inputs in flat mode.", 178 errc::invalid_argument); 179 180 if (Options.PaperTrailWarnings && Options.InputIsYAMLDebugMap) 181 return make_error<StringError>( 182 "paper trail warnings are not supported for YAML input.", 183 errc::invalid_argument); 184 185 return Error::success(); 186 } 187 188 static Expected<AccelTableKind> getAccelTableKind(opt::InputArgList &Args) { 189 if (opt::Arg *Accelerator = Args.getLastArg(OPT_accelerator)) { 190 StringRef S = Accelerator->getValue(); 191 if (S == "Apple") 192 return AccelTableKind::Apple; 193 if (S == "Dwarf") 194 return AccelTableKind::Dwarf; 195 if (S == "Default") 196 return AccelTableKind::Default; 197 return make_error<StringError>( 198 "invalid accelerator type specified: '" + S + 199 "'. Support values are 'Apple', 'Dwarf' and 'Default'.", 200 inconvertibleErrorCode()); 201 } 202 return AccelTableKind::Default; 203 } 204 205 /// Parses the command line options into the LinkOptions struct and performs 206 /// some sanity checking. Returns an error in case the latter fails. 207 static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) { 208 DsymutilOptions Options; 209 210 Options.DumpDebugMap = Args.hasArg(OPT_dump_debug_map); 211 Options.DumpStab = Args.hasArg(OPT_symtab); 212 Options.Flat = Args.hasArg(OPT_flat); 213 Options.InputIsYAMLDebugMap = Args.hasArg(OPT_yaml_input); 214 Options.PaperTrailWarnings = Args.hasArg(OPT_papertrail); 215 Options.Verify = Args.hasArg(OPT_verify); 216 217 Options.LinkOpts.Minimize = Args.hasArg(OPT_minimize); 218 Options.LinkOpts.NoODR = Args.hasArg(OPT_no_odr); 219 Options.LinkOpts.NoOutput = Args.hasArg(OPT_no_output); 220 Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp); 221 Options.LinkOpts.Update = Args.hasArg(OPT_update); 222 Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose); 223 224 if (Expected<AccelTableKind> AccelKind = getAccelTableKind(Args)) { 225 Options.LinkOpts.TheAccelTableKind = *AccelKind; 226 } else { 227 return AccelKind.takeError(); 228 } 229 230 if (opt::Arg *SymbolMap = Args.getLastArg(OPT_symbolmap)) 231 Options.SymbolMap = SymbolMap->getValue(); 232 233 if (Args.hasArg(OPT_symbolmap)) 234 Options.LinkOpts.Update = true; 235 236 if (Expected<std::vector<std::string>> InputFiles = 237 getInputs(Args, Options.LinkOpts.Update)) { 238 Options.InputFiles = std::move(*InputFiles); 239 } else { 240 return InputFiles.takeError(); 241 } 242 243 for (auto *Arch : Args.filtered(OPT_arch)) 244 Options.Archs.push_back(Arch->getValue()); 245 246 if (opt::Arg *OsoPrependPath = Args.getLastArg(OPT_oso_prepend_path)) 247 Options.LinkOpts.PrependPath = OsoPrependPath->getValue(); 248 249 if (opt::Arg *OutputFile = Args.getLastArg(OPT_output)) 250 Options.OutputFile = OutputFile->getValue(); 251 252 if (opt::Arg *Toolchain = Args.getLastArg(OPT_toolchain)) 253 Options.Toolchain = Toolchain->getValue(); 254 255 if (Args.hasArg(OPT_assembly)) 256 Options.LinkOpts.FileType = OutputFileType::Assembly; 257 258 if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads)) 259 Options.LinkOpts.Threads = atoi(NumThreads->getValue()); 260 else 261 Options.LinkOpts.Threads = thread::hardware_concurrency(); 262 263 if (Options.DumpDebugMap || Options.LinkOpts.Verbose) 264 Options.LinkOpts.Threads = 1; 265 266 if (getenv("RC_DEBUG_OPTIONS")) 267 Options.PaperTrailWarnings = true; 268 269 if (opt::Arg *RemarksPrependPath = Args.getLastArg(OPT_remarks_prepend_path)) 270 Options.LinkOpts.RemarksPrependPath = RemarksPrependPath->getValue(); 271 272 if (opt::Arg *RemarksOutputFormat = 273 Args.getLastArg(OPT_remarks_output_format)) { 274 if (Expected<remarks::Format> FormatOrErr = 275 remarks::parseFormat(RemarksOutputFormat->getValue())) 276 Options.LinkOpts.RemarksFormat = *FormatOrErr; 277 else 278 return FormatOrErr.takeError(); 279 } 280 281 if (Error E = verifyOptions(Options)) 282 return std::move(E); 283 return Options; 284 } 285 286 static Error createPlistFile(StringRef Bin, StringRef BundleRoot, 287 StringRef Toolchain) { 288 // Create plist file to write to. 289 SmallString<128> InfoPlist(BundleRoot); 290 sys::path::append(InfoPlist, "Contents/Info.plist"); 291 std::error_code EC; 292 raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_Text); 293 if (EC) 294 return make_error<StringError>( 295 "cannot create Plist: " + toString(errorCodeToError(EC)), EC); 296 297 CFBundleInfo BI = getBundleInfo(Bin); 298 299 if (BI.IDStr.empty()) { 300 StringRef BundleID = *sys::path::rbegin(BundleRoot); 301 if (sys::path::extension(BundleRoot) == ".dSYM") 302 BI.IDStr = sys::path::stem(BundleID); 303 else 304 BI.IDStr = BundleID; 305 } 306 307 // Print out information to the plist file. 308 PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n" 309 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " 310 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" 311 << "<plist version=\"1.0\">\n" 312 << "\t<dict>\n" 313 << "\t\t<key>CFBundleDevelopmentRegion</key>\n" 314 << "\t\t<string>English</string>\n" 315 << "\t\t<key>CFBundleIdentifier</key>\n" 316 << "\t\t<string>com.apple.xcode.dsym." << BI.IDStr << "</string>\n" 317 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n" 318 << "\t\t<string>6.0</string>\n" 319 << "\t\t<key>CFBundlePackageType</key>\n" 320 << "\t\t<string>dSYM</string>\n" 321 << "\t\t<key>CFBundleSignature</key>\n" 322 << "\t\t<string>\?\?\?\?</string>\n"; 323 324 if (!BI.OmitShortVersion()) { 325 PL << "\t\t<key>CFBundleShortVersionString</key>\n"; 326 PL << "\t\t<string>"; 327 printHTMLEscaped(BI.ShortVersionStr, PL); 328 PL << "</string>\n"; 329 } 330 331 PL << "\t\t<key>CFBundleVersion</key>\n"; 332 PL << "\t\t<string>"; 333 printHTMLEscaped(BI.VersionStr, PL); 334 PL << "</string>\n"; 335 336 if (!Toolchain.empty()) { 337 PL << "\t\t<key>Toolchain</key>\n"; 338 PL << "\t\t<string>"; 339 printHTMLEscaped(Toolchain, PL); 340 PL << "</string>\n"; 341 } 342 343 PL << "\t</dict>\n" 344 << "</plist>\n"; 345 346 PL.close(); 347 return Error::success(); 348 } 349 350 static Error createBundleDir(StringRef BundleBase) { 351 SmallString<128> Bundle(BundleBase); 352 sys::path::append(Bundle, "Contents", "Resources", "DWARF"); 353 if (std::error_code EC = 354 create_directories(Bundle.str(), true, sys::fs::perms::all_all)) 355 return make_error<StringError>( 356 "cannot create bundle: " + toString(errorCodeToError(EC)), EC); 357 358 return Error::success(); 359 } 360 361 static bool verify(StringRef OutputFile, StringRef Arch, bool Verbose) { 362 if (OutputFile == "-") { 363 WithColor::warning() << "verification skipped for " << Arch 364 << "because writing to stdout.\n"; 365 return true; 366 } 367 368 Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile); 369 if (!BinOrErr) { 370 WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError()); 371 return false; 372 } 373 374 Binary &Binary = *BinOrErr.get().getBinary(); 375 if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) { 376 raw_ostream &os = Verbose ? errs() : nulls(); 377 os << "Verifying DWARF for architecture: " << Arch << "\n"; 378 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj); 379 DIDumpOptions DumpOpts; 380 bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion()); 381 if (!success) 382 WithColor::error() << "verification failed for " << Arch << '\n'; 383 return success; 384 } 385 386 return false; 387 } 388 389 namespace { 390 struct OutputLocation { 391 OutputLocation(std::string DWARFFile, Optional<std::string> ResourceDir = {}) 392 : DWARFFile(DWARFFile), ResourceDir(ResourceDir) {} 393 /// This method is a workaround for older compilers. 394 Optional<std::string> getResourceDir() const { return ResourceDir; } 395 std::string DWARFFile; 396 Optional<std::string> ResourceDir; 397 }; 398 } // namespace 399 400 static Expected<OutputLocation> 401 getOutputFileName(StringRef InputFile, const DsymutilOptions &Options) { 402 if (Options.OutputFile == "-") 403 return OutputLocation(Options.OutputFile); 404 405 // When updating, do in place replacement. 406 if (Options.OutputFile.empty() && 407 (Options.LinkOpts.Update || !Options.SymbolMap.empty())) 408 return OutputLocation(InputFile); 409 410 // If a flat dSYM has been requested, things are pretty simple. 411 if (Options.Flat) { 412 if (Options.OutputFile.empty()) { 413 if (InputFile == "-") 414 return OutputLocation{"a.out.dwarf", {}}; 415 return OutputLocation((InputFile + ".dwarf").str()); 416 } 417 418 return OutputLocation(Options.OutputFile); 419 } 420 421 // We need to create/update a dSYM bundle. 422 // A bundle hierarchy looks like this: 423 // <bundle name>.dSYM/ 424 // Contents/ 425 // Info.plist 426 // Resources/ 427 // DWARF/ 428 // <DWARF file(s)> 429 std::string DwarfFile = InputFile == "-" ? StringRef("a.out") : InputFile; 430 SmallString<128> Path(Options.OutputFile); 431 if (Path.empty()) 432 Path = DwarfFile + ".dSYM"; 433 if (!Options.LinkOpts.NoOutput) { 434 if (auto E = createBundleDir(Path)) 435 return std::move(E); 436 if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain)) 437 return std::move(E); 438 } 439 440 sys::path::append(Path, "Contents", "Resources"); 441 std::string ResourceDir = Path.str(); 442 sys::path::append(Path, "DWARF", sys::path::filename(DwarfFile)); 443 return OutputLocation(Path.str(), ResourceDir); 444 } 445 446 int main(int argc, char **argv) { 447 InitLLVM X(argc, argv); 448 449 // Parse arguments. 450 DsymutilOptTable T; 451 unsigned MAI; 452 unsigned MAC; 453 ArrayRef<const char *> ArgsArr = makeArrayRef(argv + 1, argc - 1); 454 opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC); 455 456 void *P = (void *)(intptr_t)getOutputFileName; 457 std::string SDKPath = sys::fs::getMainExecutable(argv[0], P); 458 SDKPath = sys::path::parent_path(SDKPath); 459 460 for (auto *Arg : Args.filtered(OPT_UNKNOWN)) { 461 WithColor::warning() << "ignoring unknown option: " << Arg->getSpelling() 462 << '\n'; 463 } 464 465 if (Args.hasArg(OPT_help)) { 466 T.PrintHelp( 467 outs(), (std::string(argv[0]) + " [options] <input files>").c_str(), 468 "manipulate archived DWARF debug symbol files.\n\n" 469 "dsymutil links the DWARF debug information found in the object files\n" 470 "for the executable <input file> by using debug symbols information\n" 471 "contained in its symbol table.\n", 472 false); 473 return 0; 474 } 475 476 if (Args.hasArg(OPT_version)) { 477 cl::PrintVersionMessage(); 478 return 0; 479 } 480 481 auto OptionsOrErr = getOptions(Args); 482 if (!OptionsOrErr) { 483 WithColor::error() << toString(OptionsOrErr.takeError()); 484 return 1; 485 } 486 487 auto &Options = *OptionsOrErr; 488 489 InitializeAllTargetInfos(); 490 InitializeAllTargetMCs(); 491 InitializeAllTargets(); 492 InitializeAllAsmPrinters(); 493 494 for (const auto &Arch : Options.Archs) 495 if (Arch != "*" && Arch != "all" && 496 !object::MachOObjectFile::isValidArch(Arch)) { 497 WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n"; 498 return 1; 499 } 500 501 SymbolMapLoader SymMapLoader(Options.SymbolMap); 502 503 for (auto &InputFile : Options.InputFiles) { 504 // Dump the symbol table for each input file and requested arch 505 if (Options.DumpStab) { 506 if (!dumpStab(InputFile, Options.Archs, Options.LinkOpts.PrependPath)) 507 return 1; 508 continue; 509 } 510 511 auto DebugMapPtrsOrErr = 512 parseDebugMap(InputFile, Options.Archs, Options.LinkOpts.PrependPath, 513 Options.PaperTrailWarnings, Options.LinkOpts.Verbose, 514 Options.InputIsYAMLDebugMap); 515 516 if (auto EC = DebugMapPtrsOrErr.getError()) { 517 WithColor::error() << "cannot parse the debug map for '" << InputFile 518 << "': " << EC.message() << '\n'; 519 return 1; 520 } 521 522 // Remember the number of debug maps that are being processed to decide how 523 // to name the remark files. 524 Options.LinkOpts.NumDebugMaps = DebugMapPtrsOrErr->size(); 525 526 if (Options.LinkOpts.Update) { 527 // The debug map should be empty. Add one object file corresponding to 528 // the input file. 529 for (auto &Map : *DebugMapPtrsOrErr) 530 Map->addDebugMapObject(InputFile, 531 sys::TimePoint<std::chrono::seconds>()); 532 } 533 534 // Ensure that the debug map is not empty (anymore). 535 if (DebugMapPtrsOrErr->empty()) { 536 WithColor::error() << "no architecture to link\n"; 537 return 1; 538 } 539 540 // Shared a single binary holder for all the link steps. 541 BinaryHolder BinHolder; 542 543 unsigned ThreadCount = 544 std::min<unsigned>(Options.LinkOpts.Threads, DebugMapPtrsOrErr->size()); 545 ThreadPool Threads(ThreadCount); 546 547 // If there is more than one link to execute, we need to generate 548 // temporary files. 549 const bool NeedsTempFiles = 550 !Options.DumpDebugMap && (Options.OutputFile != "-") && 551 (DebugMapPtrsOrErr->size() != 1 || Options.LinkOpts.Update); 552 const bool Verify = Options.Verify && !Options.LinkOpts.NoOutput; 553 554 SmallVector<MachOUtils::ArchAndFile, 4> TempFiles; 555 std::atomic_char AllOK(1); 556 for (auto &Map : *DebugMapPtrsOrErr) { 557 if (Options.LinkOpts.Verbose || Options.DumpDebugMap) 558 Map->print(outs()); 559 560 if (Options.DumpDebugMap) 561 continue; 562 563 if (!Options.SymbolMap.empty()) 564 Options.LinkOpts.Translator = SymMapLoader.Load(InputFile, *Map); 565 566 if (Map->begin() == Map->end()) 567 WithColor::warning() 568 << "no debug symbols in executable (-arch " 569 << MachOUtils::getArchName(Map->getTriple().getArchName()) << ")\n"; 570 571 // Using a std::shared_ptr rather than std::unique_ptr because move-only 572 // types don't work with std::bind in the ThreadPool implementation. 573 std::shared_ptr<raw_fd_ostream> OS; 574 575 Expected<OutputLocation> OutputLocationOrErr = 576 getOutputFileName(InputFile, Options); 577 if (!OutputLocationOrErr) { 578 WithColor::error() << toString(OutputLocationOrErr.takeError()); 579 return 1; 580 } 581 Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir(); 582 583 std::string OutputFile = OutputLocationOrErr->DWARFFile; 584 if (NeedsTempFiles) { 585 TempFiles.emplace_back(Map->getTriple().getArchName().str()); 586 587 auto E = TempFiles.back().createTempFile(); 588 if (E) { 589 WithColor::error() << toString(std::move(E)); 590 return 1; 591 } 592 593 auto &TempFile = *(TempFiles.back().File); 594 OS = std::make_shared<raw_fd_ostream>(TempFile.FD, 595 /*shouldClose*/ false); 596 OutputFile = TempFile.TmpName; 597 } else { 598 std::error_code EC; 599 OS = std::make_shared<raw_fd_ostream>( 600 Options.LinkOpts.NoOutput ? "-" : OutputFile, EC, sys::fs::OF_None); 601 if (EC) { 602 WithColor::error() << OutputFile << ": " << EC.message(); 603 return 1; 604 } 605 } 606 607 auto LinkLambda = [&, OutputFile](std::shared_ptr<raw_fd_ostream> Stream, 608 LinkOptions Options) { 609 AllOK.fetch_and( 610 linkDwarf(*Stream, BinHolder, *Map, std::move(Options))); 611 Stream->flush(); 612 if (Verify) 613 AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName(), 614 Options.Verbose)); 615 }; 616 617 // FIXME: The DwarfLinker can have some very deep recursion that can max 618 // out the (significantly smaller) stack when using threads. We don't 619 // want this limitation when we only have a single thread. 620 if (ThreadCount == 1) 621 LinkLambda(OS, Options.LinkOpts); 622 else 623 Threads.async(LinkLambda, OS, Options.LinkOpts); 624 } 625 626 Threads.wait(); 627 628 if (!AllOK) 629 return 1; 630 631 if (NeedsTempFiles) { 632 Expected<OutputLocation> OutputLocationOrErr = 633 getOutputFileName(InputFile, Options); 634 if (!OutputLocationOrErr) { 635 WithColor::error() << toString(OutputLocationOrErr.takeError()); 636 return 1; 637 } 638 if (!MachOUtils::generateUniversalBinary(TempFiles, 639 OutputLocationOrErr->DWARFFile, 640 Options.LinkOpts, SDKPath)) 641 return 1; 642 } 643 } 644 645 return 0; 646 } 647