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