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