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