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/FileCollector.h" 37 #include "llvm/Support/FileSystem.h" 38 #include "llvm/Support/InitLLVM.h" 39 #include "llvm/Support/ManagedStatic.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::Off; 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<AccelTableKind> getAccelTableKind(opt::InputArgList &Args) { 210 if (opt::Arg *Accelerator = Args.getLastArg(OPT_accelerator)) { 211 StringRef S = Accelerator->getValue(); 212 if (S == "Apple") 213 return AccelTableKind::Apple; 214 if (S == "Dwarf") 215 return AccelTableKind::Dwarf; 216 if (S == "Pub") 217 return AccelTableKind::Pub; 218 if (S == "Default") 219 return AccelTableKind::Default; 220 return make_error<StringError>( 221 "invalid accelerator type specified: '" + S + 222 "'. Support values are 'Apple', 'Dwarf', 'Pub' and 'Default'.", 223 inconvertibleErrorCode()); 224 } 225 return AccelTableKind::Default; 226 } 227 228 static Expected<DWARFVerify> getVerifyKind(opt::InputArgList &Args) { 229 if (Args.hasArg(OPT_verify)) 230 return DWARFVerify::Output; 231 if (opt::Arg *Verify = Args.getLastArg(OPT_verify_dwarf)) { 232 StringRef S = Verify->getValue(); 233 if (S == "input") 234 return DWARFVerify::Input; 235 if (S == "output") 236 return DWARFVerify::Output; 237 if (S == "all") 238 return DWARFVerify::All; 239 if (S == "none") 240 return DWARFVerify::None; 241 return make_error<StringError>( 242 "invalid verify type specified: '" + S + 243 "'. Support values are 'input', 'output', 'all' and 'none'.", 244 inconvertibleErrorCode()); 245 } 246 return DWARFVerify::None; 247 } 248 249 /// Parses the command line options into the LinkOptions struct and performs 250 /// some sanity checking. Returns an error in case the latter fails. 251 static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) { 252 DsymutilOptions Options; 253 254 Options.DumpDebugMap = Args.hasArg(OPT_dump_debug_map); 255 Options.DumpStab = Args.hasArg(OPT_symtab); 256 Options.Flat = Args.hasArg(OPT_flat); 257 Options.InputIsYAMLDebugMap = Args.hasArg(OPT_yaml_input); 258 Options.PaperTrailWarnings = Args.hasArg(OPT_papertrail); 259 260 if (Expected<DWARFVerify> Verify = getVerifyKind(Args)) { 261 Options.Verify = *Verify; 262 } else { 263 return Verify.takeError(); 264 } 265 266 Options.LinkOpts.NoODR = Args.hasArg(OPT_no_odr); 267 Options.LinkOpts.VerifyInputDWARF = 268 flagIsSet(Options.Verify, DWARFVerify::Input); 269 Options.LinkOpts.NoOutput = Args.hasArg(OPT_no_output); 270 Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp); 271 Options.LinkOpts.Update = Args.hasArg(OPT_update); 272 Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose); 273 Options.LinkOpts.Statistics = Args.hasArg(OPT_statistics); 274 Options.LinkOpts.KeepFunctionForStatic = 275 Args.hasArg(OPT_keep_func_for_static); 276 277 if (opt::Arg *ReproducerPath = Args.getLastArg(OPT_use_reproducer)) { 278 Options.ReproMode = ReproducerMode::Use; 279 Options.ReproducerPath = ReproducerPath->getValue(); 280 } 281 282 if (Args.hasArg(OPT_gen_reproducer)) 283 Options.ReproMode = ReproducerMode::Generate; 284 285 if (Expected<AccelTableKind> AccelKind = getAccelTableKind(Args)) { 286 Options.LinkOpts.TheAccelTableKind = *AccelKind; 287 } else { 288 return AccelKind.takeError(); 289 } 290 291 if (opt::Arg *SymbolMap = Args.getLastArg(OPT_symbolmap)) 292 Options.SymbolMap = SymbolMap->getValue(); 293 294 if (Args.hasArg(OPT_symbolmap)) 295 Options.LinkOpts.Update = true; 296 297 if (Expected<std::vector<std::string>> InputFiles = 298 getInputs(Args, Options.LinkOpts.Update)) { 299 Options.InputFiles = std::move(*InputFiles); 300 } else { 301 return InputFiles.takeError(); 302 } 303 304 for (auto *Arch : Args.filtered(OPT_arch)) 305 Options.Archs.push_back(Arch->getValue()); 306 307 if (opt::Arg *OsoPrependPath = Args.getLastArg(OPT_oso_prepend_path)) 308 Options.LinkOpts.PrependPath = OsoPrependPath->getValue(); 309 310 for (const auto &Arg : Args.getAllArgValues(OPT_object_prefix_map)) { 311 auto Split = StringRef(Arg).split('='); 312 Options.LinkOpts.ObjectPrefixMap.insert( 313 {std::string(Split.first), std::string(Split.second)}); 314 } 315 316 if (opt::Arg *OutputFile = Args.getLastArg(OPT_output)) 317 Options.OutputFile = OutputFile->getValue(); 318 319 if (opt::Arg *Toolchain = Args.getLastArg(OPT_toolchain)) 320 Options.Toolchain = Toolchain->getValue(); 321 322 if (Args.hasArg(OPT_assembly)) 323 Options.LinkOpts.FileType = OutputFileType::Assembly; 324 325 if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads)) 326 Options.LinkOpts.Threads = atoi(NumThreads->getValue()); 327 else 328 Options.LinkOpts.Threads = 0; // Use all available hardware threads 329 330 if (Options.DumpDebugMap || Options.LinkOpts.Verbose) 331 Options.LinkOpts.Threads = 1; 332 333 if (getenv("RC_DEBUG_OPTIONS")) 334 Options.PaperTrailWarnings = true; 335 336 if (opt::Arg *RemarksPrependPath = Args.getLastArg(OPT_remarks_prepend_path)) 337 Options.LinkOpts.RemarksPrependPath = RemarksPrependPath->getValue(); 338 339 if (opt::Arg *RemarksOutputFormat = 340 Args.getLastArg(OPT_remarks_output_format)) { 341 if (Expected<remarks::Format> FormatOrErr = 342 remarks::parseFormat(RemarksOutputFormat->getValue())) 343 Options.LinkOpts.RemarksFormat = *FormatOrErr; 344 else 345 return FormatOrErr.takeError(); 346 } 347 348 if (Error E = verifyOptions(Options)) 349 return std::move(E); 350 return Options; 351 } 352 353 static Error createPlistFile(StringRef Bin, StringRef BundleRoot, 354 StringRef Toolchain) { 355 // Create plist file to write to. 356 SmallString<128> InfoPlist(BundleRoot); 357 sys::path::append(InfoPlist, "Contents/Info.plist"); 358 std::error_code EC; 359 raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_TextWithCRLF); 360 if (EC) 361 return make_error<StringError>( 362 "cannot create Plist: " + toString(errorCodeToError(EC)), EC); 363 364 CFBundleInfo BI = getBundleInfo(Bin); 365 366 if (BI.IDStr.empty()) { 367 StringRef BundleID = *sys::path::rbegin(BundleRoot); 368 if (sys::path::extension(BundleRoot) == ".dSYM") 369 BI.IDStr = std::string(sys::path::stem(BundleID)); 370 else 371 BI.IDStr = std::string(BundleID); 372 } 373 374 // Print out information to the plist file. 375 PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n" 376 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" " 377 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n" 378 << "<plist version=\"1.0\">\n" 379 << "\t<dict>\n" 380 << "\t\t<key>CFBundleDevelopmentRegion</key>\n" 381 << "\t\t<string>English</string>\n" 382 << "\t\t<key>CFBundleIdentifier</key>\n" 383 << "\t\t<string>com.apple.xcode.dsym."; 384 printHTMLEscaped(BI.IDStr, PL); 385 PL << "</string>\n" 386 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n" 387 << "\t\t<string>6.0</string>\n" 388 << "\t\t<key>CFBundlePackageType</key>\n" 389 << "\t\t<string>dSYM</string>\n" 390 << "\t\t<key>CFBundleSignature</key>\n" 391 << "\t\t<string>\?\?\?\?</string>\n"; 392 393 if (!BI.OmitShortVersion()) { 394 PL << "\t\t<key>CFBundleShortVersionString</key>\n"; 395 PL << "\t\t<string>"; 396 printHTMLEscaped(BI.ShortVersionStr, PL); 397 PL << "</string>\n"; 398 } 399 400 PL << "\t\t<key>CFBundleVersion</key>\n"; 401 PL << "\t\t<string>"; 402 printHTMLEscaped(BI.VersionStr, PL); 403 PL << "</string>\n"; 404 405 if (!Toolchain.empty()) { 406 PL << "\t\t<key>Toolchain</key>\n"; 407 PL << "\t\t<string>"; 408 printHTMLEscaped(Toolchain, PL); 409 PL << "</string>\n"; 410 } 411 412 PL << "\t</dict>\n" 413 << "</plist>\n"; 414 415 PL.close(); 416 return Error::success(); 417 } 418 419 static Error createBundleDir(StringRef BundleBase) { 420 SmallString<128> Bundle(BundleBase); 421 sys::path::append(Bundle, "Contents", "Resources", "DWARF"); 422 if (std::error_code EC = 423 create_directories(Bundle.str(), true, sys::fs::perms::all_all)) 424 return make_error<StringError>( 425 "cannot create bundle: " + toString(errorCodeToError(EC)), EC); 426 427 return Error::success(); 428 } 429 430 static bool verifyOutput(StringRef OutputFile, StringRef Arch, bool Verbose) { 431 if (OutputFile == "-") { 432 WithColor::warning() << "verification skipped for " << Arch 433 << "because writing to stdout.\n"; 434 return true; 435 } 436 437 Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile); 438 if (!BinOrErr) { 439 WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError()); 440 return false; 441 } 442 443 Binary &Binary = *BinOrErr.get().getBinary(); 444 if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) { 445 raw_ostream &os = Verbose ? errs() : nulls(); 446 os << "Verifying DWARF for architecture: " << Arch << "\n"; 447 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj); 448 DIDumpOptions DumpOpts; 449 bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion()); 450 if (!success) 451 WithColor::error() << "output verification failed for " << Arch << '\n'; 452 return success; 453 } 454 455 return false; 456 } 457 458 namespace { 459 struct OutputLocation { 460 OutputLocation(std::string DWARFFile, Optional<std::string> ResourceDir = {}) 461 : DWARFFile(DWARFFile), ResourceDir(ResourceDir) {} 462 /// This method is a workaround for older compilers. 463 Optional<std::string> getResourceDir() const { return ResourceDir; } 464 std::string DWARFFile; 465 Optional<std::string> ResourceDir; 466 }; 467 } // namespace 468 469 static Expected<OutputLocation> 470 getOutputFileName(StringRef InputFile, const DsymutilOptions &Options) { 471 if (Options.OutputFile == "-") 472 return OutputLocation(Options.OutputFile); 473 474 // When updating, do in place replacement. 475 if (Options.OutputFile.empty() && 476 (Options.LinkOpts.Update || !Options.SymbolMap.empty())) 477 return OutputLocation(std::string(InputFile)); 478 479 // When dumping the debug map, just return an empty output location. This 480 // allows us to compute the output location once. 481 if (Options.DumpDebugMap) 482 return OutputLocation(""); 483 484 // If a flat dSYM has been requested, things are pretty simple. 485 if (Options.Flat) { 486 if (Options.OutputFile.empty()) { 487 if (InputFile == "-") 488 return OutputLocation{"a.out.dwarf", {}}; 489 return OutputLocation((InputFile + ".dwarf").str()); 490 } 491 492 return OutputLocation(Options.OutputFile); 493 } 494 495 // We need to create/update a dSYM bundle. 496 // A bundle hierarchy looks like this: 497 // <bundle name>.dSYM/ 498 // Contents/ 499 // Info.plist 500 // Resources/ 501 // DWARF/ 502 // <DWARF file(s)> 503 std::string DwarfFile = 504 std::string(InputFile == "-" ? StringRef("a.out") : InputFile); 505 SmallString<128> Path(Options.OutputFile); 506 if (Path.empty()) 507 Path = DwarfFile + ".dSYM"; 508 if (!Options.LinkOpts.NoOutput) { 509 if (auto E = createBundleDir(Path)) 510 return std::move(E); 511 if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain)) 512 return std::move(E); 513 } 514 515 sys::path::append(Path, "Contents", "Resources"); 516 std::string ResourceDir = std::string(Path.str()); 517 sys::path::append(Path, "DWARF", sys::path::filename(DwarfFile)); 518 return OutputLocation(std::string(Path.str()), ResourceDir); 519 } 520 521 int main(int argc, char **argv) { 522 InitLLVM X(argc, argv); 523 524 // Parse arguments. 525 DsymutilOptTable T; 526 unsigned MAI; 527 unsigned MAC; 528 ArrayRef<const char *> ArgsArr = makeArrayRef(argv + 1, argc - 1); 529 opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC); 530 531 void *P = (void *)(intptr_t)getOutputFileName; 532 std::string SDKPath = sys::fs::getMainExecutable(argv[0], P); 533 SDKPath = std::string(sys::path::parent_path(SDKPath)); 534 535 for (auto *Arg : Args.filtered(OPT_UNKNOWN)) { 536 WithColor::warning() << "ignoring unknown option: " << Arg->getSpelling() 537 << '\n'; 538 } 539 540 if (Args.hasArg(OPT_help)) { 541 T.printHelp( 542 outs(), (std::string(argv[0]) + " [options] <input files>").c_str(), 543 "manipulate archived DWARF debug symbol files.\n\n" 544 "dsymutil links the DWARF debug information found in the object files\n" 545 "for the executable <input file> by using debug symbols information\n" 546 "contained in its symbol table.\n", 547 false); 548 return EXIT_SUCCESS; 549 } 550 551 if (Args.hasArg(OPT_version)) { 552 cl::PrintVersionMessage(); 553 return EXIT_SUCCESS; 554 } 555 556 auto OptionsOrErr = getOptions(Args); 557 if (!OptionsOrErr) { 558 WithColor::error() << toString(OptionsOrErr.takeError()); 559 return EXIT_FAILURE; 560 } 561 562 auto &Options = *OptionsOrErr; 563 564 InitializeAllTargetInfos(); 565 InitializeAllTargetMCs(); 566 InitializeAllTargets(); 567 InitializeAllAsmPrinters(); 568 569 auto Repro = 570 Reproducer::createReproducer(Options.ReproMode, Options.ReproducerPath); 571 if (!Repro) { 572 WithColor::error() << toString(Repro.takeError()); 573 return EXIT_FAILURE; 574 } 575 576 Options.LinkOpts.VFS = (*Repro)->getVFS(); 577 578 for (const auto &Arch : Options.Archs) 579 if (Arch != "*" && Arch != "all" && 580 !object::MachOObjectFile::isValidArch(Arch)) { 581 WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n"; 582 return EXIT_FAILURE; 583 } 584 585 SymbolMapLoader SymMapLoader(Options.SymbolMap); 586 587 for (auto &InputFile : Options.InputFiles) { 588 // Dump the symbol table for each input file and requested arch 589 if (Options.DumpStab) { 590 if (!dumpStab(Options.LinkOpts.VFS, InputFile, Options.Archs, 591 Options.LinkOpts.PrependPath)) 592 return EXIT_FAILURE; 593 continue; 594 } 595 596 auto DebugMapPtrsOrErr = 597 parseDebugMap(Options.LinkOpts.VFS, InputFile, Options.Archs, 598 Options.LinkOpts.PrependPath, Options.PaperTrailWarnings, 599 Options.LinkOpts.Verbose, Options.InputIsYAMLDebugMap); 600 601 if (auto EC = DebugMapPtrsOrErr.getError()) { 602 WithColor::error() << "cannot parse the debug map for '" << InputFile 603 << "': " << EC.message() << '\n'; 604 return EXIT_FAILURE; 605 } 606 607 // Remember the number of debug maps that are being processed to decide how 608 // to name the remark files. 609 Options.LinkOpts.NumDebugMaps = DebugMapPtrsOrErr->size(); 610 611 if (Options.LinkOpts.Update) { 612 // The debug map should be empty. Add one object file corresponding to 613 // the input file. 614 for (auto &Map : *DebugMapPtrsOrErr) 615 Map->addDebugMapObject(InputFile, 616 sys::TimePoint<std::chrono::seconds>()); 617 } 618 619 // Ensure that the debug map is not empty (anymore). 620 if (DebugMapPtrsOrErr->empty()) { 621 WithColor::error() << "no architecture to link\n"; 622 return EXIT_FAILURE; 623 } 624 625 // Shared a single binary holder for all the link steps. 626 BinaryHolder BinHolder(Options.LinkOpts.VFS); 627 628 // Compute the output location and update the resource directory. 629 Expected<OutputLocation> OutputLocationOrErr = 630 getOutputFileName(InputFile, Options); 631 if (!OutputLocationOrErr) { 632 WithColor::error() << toString(OutputLocationOrErr.takeError()); 633 return EXIT_FAILURE; 634 } 635 Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir(); 636 637 // Statistics only require different architectures to be processed 638 // sequentially, the link itself can still happen in parallel. Change the 639 // thread pool strategy here instead of modifying LinkOpts.Threads. 640 ThreadPoolStrategy S = hardware_concurrency( 641 Options.LinkOpts.Statistics ? 1 : Options.LinkOpts.Threads); 642 if (Options.LinkOpts.Threads == 0) { 643 // If NumThreads is not specified, create one thread for each input, up to 644 // the number of hardware threads. 645 S.ThreadsRequested = DebugMapPtrsOrErr->size(); 646 S.Limit = true; 647 } 648 ThreadPool Threads(S); 649 650 // If there is more than one link to execute, we need to generate 651 // temporary files. 652 const bool NeedsTempFiles = 653 !Options.DumpDebugMap && (Options.OutputFile != "-") && 654 (DebugMapPtrsOrErr->size() != 1 || Options.LinkOpts.Update); 655 bool VerifyOutput = flagIsSet(Options.Verify, DWARFVerify::Output); 656 if (VerifyOutput && Options.LinkOpts.NoOutput) { 657 WithColor::warning() 658 << "skipping output verification because --no-output was passed\n"; 659 VerifyOutput = false; 660 } 661 662 SmallVector<MachOUtils::ArchAndFile, 4> TempFiles; 663 std::atomic_char AllOK(1); 664 for (auto &Map : *DebugMapPtrsOrErr) { 665 if (Options.LinkOpts.Verbose || Options.DumpDebugMap) 666 Map->print(outs()); 667 668 if (Options.DumpDebugMap) 669 continue; 670 671 if (!Options.SymbolMap.empty()) 672 Options.LinkOpts.Translator = SymMapLoader.Load(InputFile, *Map); 673 674 if (Map->begin() == Map->end()) 675 WithColor::warning() 676 << "no debug symbols in executable (-arch " 677 << MachOUtils::getArchName(Map->getTriple().getArchName()) << ")\n"; 678 679 // Using a std::shared_ptr rather than std::unique_ptr because move-only 680 // types don't work with std::bind in the ThreadPool implementation. 681 std::shared_ptr<raw_fd_ostream> OS; 682 683 std::string OutputFile = OutputLocationOrErr->DWARFFile; 684 if (NeedsTempFiles) { 685 TempFiles.emplace_back(Map->getTriple().getArchName().str()); 686 687 auto E = TempFiles.back().createTempFile(); 688 if (E) { 689 WithColor::error() << toString(std::move(E)); 690 return EXIT_FAILURE; 691 } 692 693 auto &TempFile = *(TempFiles.back().File); 694 OS = std::make_shared<raw_fd_ostream>(TempFile.FD, 695 /*shouldClose*/ false); 696 OutputFile = TempFile.TmpName; 697 } else { 698 std::error_code EC; 699 OS = std::make_shared<raw_fd_ostream>( 700 Options.LinkOpts.NoOutput ? "-" : OutputFile, EC, sys::fs::OF_None); 701 if (EC) { 702 WithColor::error() << OutputFile << ": " << EC.message(); 703 return EXIT_FAILURE; 704 } 705 } 706 707 auto LinkLambda = [&, OutputFile](std::shared_ptr<raw_fd_ostream> Stream, 708 LinkOptions Options) { 709 AllOK.fetch_and( 710 linkDwarf(*Stream, BinHolder, *Map, std::move(Options))); 711 Stream->flush(); 712 if (VerifyOutput) { 713 AllOK.fetch_and(verifyOutput( 714 OutputFile, Map->getTriple().getArchName(), Options.Verbose)); 715 } 716 }; 717 718 // FIXME: The DwarfLinker can have some very deep recursion that can max 719 // out the (significantly smaller) stack when using threads. We don't 720 // want this limitation when we only have a single thread. 721 if (S.ThreadsRequested == 1) 722 LinkLambda(OS, Options.LinkOpts); 723 else 724 Threads.async(LinkLambda, OS, Options.LinkOpts); 725 } 726 727 Threads.wait(); 728 729 if (!AllOK) 730 return EXIT_FAILURE; 731 732 if (NeedsTempFiles) { 733 // Universal Mach-O files can't have an archicture slice that starts 734 // beyond the 4GB boundary. "lipo" can creeate a 64 bit universal header, 735 // but not all tools can parse these files so we want to return an error 736 // if the file can't be encoded as a file with a 32 bit universal header. 737 // To detect this, we check the size of each architecture's skinny Mach-O 738 // file and add up the offsets. If they exceed 4GB, then we return an 739 // error. 740 741 // First we compute the right offset where the first architecture will fit 742 // followin the 32 bit universal header. The 32 bit universal header 743 // starts with a uint32_t magic and a uint32_t number of architecture 744 // infos. Then it is followed by 5 uint32_t values for each architecture. 745 // So we set the start offset to the right value so we can calculate the 746 // exact offset that the first architecture slice can start at. 747 constexpr uint64_t MagicAndCountSize = 2 * 4; 748 constexpr uint64_t UniversalArchInfoSize = 5 * 4; 749 uint64_t FileOffset = MagicAndCountSize + 750 UniversalArchInfoSize * TempFiles.size(); 751 for (const auto &File: TempFiles) { 752 ErrorOr<vfs::Status> stat = Options.LinkOpts.VFS->status(File.path()); 753 if (!stat) 754 break; 755 FileOffset += stat->getSize(); 756 if (FileOffset > UINT32_MAX) { 757 WithColor::error() << "the univesral binary has a slice with an " 758 "offset exceeds 4GB and will produce an invalid Mach-O file."; 759 return EXIT_FAILURE; 760 } 761 } 762 if (!MachOUtils::generateUniversalBinary(TempFiles, 763 OutputLocationOrErr->DWARFFile, 764 Options.LinkOpts, SDKPath)) 765 return EXIT_FAILURE; 766 } 767 } 768 769 return EXIT_SUCCESS; 770 } 771