578 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			578 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===- dsymutil.cpp - Debug info dumping utility for llvm -----------------===//
 | |
| //
 | |
| //                     The LLVM Compiler Infrastructure
 | |
| //
 | |
| // This file is distributed under the University of Illinois Open Source
 | |
| // License. See LICENSE.TXT for details.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| //
 | |
| // This program is a utility that aims to be a dropin replacement for
 | |
| // Darwin's dsymutil.
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "dsymutil.h"
 | |
| #include "BinaryHolder.h"
 | |
| #include "CFBundle.h"
 | |
| #include "DebugMap.h"
 | |
| #include "LinkUtils.h"
 | |
| #include "MachOUtils.h"
 | |
| #include "llvm/ADT/SmallString.h"
 | |
| #include "llvm/ADT/SmallVector.h"
 | |
| #include "llvm/ADT/StringExtras.h"
 | |
| #include "llvm/ADT/StringRef.h"
 | |
| #include "llvm/ADT/Triple.h"
 | |
| #include "llvm/DebugInfo/DIContext.h"
 | |
| #include "llvm/DebugInfo/DWARF/DWARFContext.h"
 | |
| #include "llvm/DebugInfo/DWARF/DWARFVerifier.h"
 | |
| #include "llvm/Object/Binary.h"
 | |
| #include "llvm/Object/MachO.h"
 | |
| #include "llvm/Support/CommandLine.h"
 | |
| #include "llvm/Support/FileSystem.h"
 | |
| #include "llvm/Support/InitLLVM.h"
 | |
| #include "llvm/Support/ManagedStatic.h"
 | |
| #include "llvm/Support/Path.h"
 | |
| #include "llvm/Support/TargetSelect.h"
 | |
| #include "llvm/Support/ThreadPool.h"
 | |
| #include "llvm/Support/WithColor.h"
 | |
| #include "llvm/Support/raw_ostream.h"
 | |
| #include "llvm/Support/thread.h"
 | |
| #include <algorithm>
 | |
| #include <cstdint>
 | |
| #include <cstdlib>
 | |
| #include <string>
 | |
| #include <system_error>
 | |
| 
 | |
| using namespace llvm;
 | |
| using namespace llvm::cl;
 | |
| using namespace llvm::dsymutil;
 | |
| using namespace object;
 | |
| 
 | |
| static OptionCategory DsymCategory("Specific Options");
 | |
| static opt<bool> Help("h", desc("Alias for -help"), Hidden);
 | |
| static opt<bool> Version("v", desc("Alias for -version"), Hidden);
 | |
| 
 | |
| static list<std::string> InputFiles(Positional, OneOrMore,
 | |
|                                     desc("<input files>"), cat(DsymCategory));
 | |
| 
 | |
| static opt<std::string>
 | |
|     OutputFileOpt("o",
 | |
|                   desc("Specify the output file. default: <input file>.dwarf"),
 | |
|                   value_desc("filename"), cat(DsymCategory));
 | |
| 
 | |
| static opt<std::string> OsoPrependPath(
 | |
|     "oso-prepend-path",
 | |
|     desc("Specify a directory to prepend to the paths of object files."),
 | |
|     value_desc("path"), cat(DsymCategory));
 | |
| 
 | |
| static opt<bool> Assembly(
 | |
|     "S",
 | |
|     desc("Output textual assembly instead of a binary dSYM companion file."),
 | |
|     init(false), cat(DsymCategory), cl::Hidden);
 | |
| 
 | |
| static opt<bool> DumpStab(
 | |
|     "symtab",
 | |
|     desc("Dumps the symbol table found in executable or object file(s) and\n"
 | |
|          "exits."),
 | |
|     init(false), cat(DsymCategory));
 | |
| static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab));
 | |
| 
 | |
| static opt<bool> FlatOut("flat",
 | |
|                          desc("Produce a flat dSYM file (not a bundle)."),
 | |
|                          init(false), cat(DsymCategory));
 | |
| static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut));
 | |
| 
 | |
| static opt<bool> Minimize(
 | |
|     "minimize",
 | |
|     desc("When used when creating a dSYM file with Apple accelerator tables,\n"
 | |
|          "this option will suppress the emission of the .debug_inlines, \n"
 | |
|          ".debug_pubnames, and .debug_pubtypes sections since dsymutil \n"
 | |
|          "has better equivalents: .apple_names and .apple_types. When used in\n"
 | |
|          "conjunction with --update option, this option will cause redundant\n"
 | |
|          "accelerator tables to be removed."),
 | |
|     init(false), cat(DsymCategory));
 | |
| static alias MinimizeA("z", desc("Alias for --minimize"), aliasopt(Minimize));
 | |
| 
 | |
| static opt<bool> Update(
 | |
|     "update",
 | |
|     desc("Updates existing dSYM files to contain the latest accelerator\n"
 | |
|          "tables and other DWARF optimizations."),
 | |
|     init(false), cat(DsymCategory));
 | |
| static alias UpdateA("u", desc("Alias for --update"), aliasopt(Update));
 | |
| 
 | |
| static cl::opt<AccelTableKind> AcceleratorTable(
 | |
|     "accelerator", cl::desc("Output accelerator tables."),
 | |
|     cl::values(clEnumValN(AccelTableKind::Default, "Default",
 | |
|                           "Default for input."),
 | |
|                clEnumValN(AccelTableKind::Apple, "Apple", "Apple"),
 | |
|                clEnumValN(AccelTableKind::Dwarf, "Dwarf", "DWARF")),
 | |
|     cl::init(AccelTableKind::Default), cat(DsymCategory));
 | |
| 
 | |
| static opt<unsigned> NumThreads(
 | |
|     "num-threads",
 | |
|     desc("Specifies the maximum number (n) of simultaneous threads to use\n"
 | |
|          "when linking multiple architectures."),
 | |
|     value_desc("n"), init(0), cat(DsymCategory));
 | |
| static alias NumThreadsA("j", desc("Alias for --num-threads"),
 | |
|                          aliasopt(NumThreads));
 | |
| 
 | |
| static opt<bool> Verbose("verbose", desc("Verbosity level"), init(false),
 | |
|                          cat(DsymCategory));
 | |
| 
 | |
| static opt<bool>
 | |
|     NoOutput("no-output",
 | |
|              desc("Do the link in memory, but do not emit the result file."),
 | |
|              init(false), cat(DsymCategory));
 | |
| 
 | |
| static opt<bool>
 | |
|     NoTimestamp("no-swiftmodule-timestamp",
 | |
|                 desc("Don't check timestamp for swiftmodule files."),
 | |
|                 init(false), cat(DsymCategory));
 | |
| 
 | |
| static list<std::string> ArchFlags(
 | |
|     "arch",
 | |
|     desc("Link DWARF debug information only for specified CPU architecture\n"
 | |
|          "types. This option can be specified multiple times, once for each\n"
 | |
|          "desired architecture. All CPU architectures will be linked by\n"
 | |
|          "default."),
 | |
|     value_desc("arch"), ZeroOrMore, cat(DsymCategory));
 | |
| 
 | |
| static opt<bool>
 | |
|     NoODR("no-odr",
 | |
|           desc("Do not use ODR (One Definition Rule) for type uniquing."),
 | |
|           init(false), cat(DsymCategory));
 | |
| 
 | |
| static opt<bool> DumpDebugMap(
 | |
|     "dump-debug-map",
 | |
|     desc("Parse and dump the debug map to standard output. Not DWARF link "
 | |
|          "will take place."),
 | |
|     init(false), cat(DsymCategory));
 | |
| 
 | |
| static opt<bool> InputIsYAMLDebugMap(
 | |
|     "y", desc("Treat the input file is a YAML debug map rather than a binary."),
 | |
|     init(false), cat(DsymCategory));
 | |
| 
 | |
| static opt<bool> Verify("verify", desc("Verify the linked DWARF debug info."),
 | |
|                         cat(DsymCategory));
 | |
| 
 | |
| static opt<std::string>
 | |
|     Toolchain("toolchain", desc("Embed toolchain information in dSYM bundle."),
 | |
|               cat(DsymCategory));
 | |
| 
 | |
| static opt<bool>
 | |
|     PaperTrailWarnings("papertrail",
 | |
|                        desc("Embed warnings in the linked DWARF debug info."),
 | |
|                        cat(DsymCategory));
 | |
| 
 | |
| static bool createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) {
 | |
|   if (NoOutput)
 | |
|     return true;
 | |
| 
 | |
|   // Create plist file to write to.
 | |
|   llvm::SmallString<128> InfoPlist(BundleRoot);
 | |
|   llvm::sys::path::append(InfoPlist, "Contents/Info.plist");
 | |
|   std::error_code EC;
 | |
|   llvm::raw_fd_ostream PL(InfoPlist, EC, llvm::sys::fs::F_Text);
 | |
|   if (EC) {
 | |
|     WithColor::error() << "cannot create plist file " << InfoPlist << ": "
 | |
|                        << EC.message() << '\n';
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   CFBundleInfo BI = getBundleInfo(Bin);
 | |
| 
 | |
|   if (BI.IDStr.empty()) {
 | |
|     llvm::StringRef BundleID = *llvm::sys::path::rbegin(BundleRoot);
 | |
|     if (llvm::sys::path::extension(BundleRoot) == ".dSYM")
 | |
|       BI.IDStr = llvm::sys::path::stem(BundleID);
 | |
|     else
 | |
|       BI.IDStr = BundleID;
 | |
|   }
 | |
| 
 | |
|   // Print out information to the plist file.
 | |
|   PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
 | |
|      << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
 | |
|      << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
 | |
|      << "<plist version=\"1.0\">\n"
 | |
|      << "\t<dict>\n"
 | |
|      << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
 | |
|      << "\t\t<string>English</string>\n"
 | |
|      << "\t\t<key>CFBundleIdentifier</key>\n"
 | |
|      << "\t\t<string>com.apple.xcode.dsym." << BI.IDStr << "</string>\n"
 | |
|      << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
 | |
|      << "\t\t<string>6.0</string>\n"
 | |
|      << "\t\t<key>CFBundlePackageType</key>\n"
 | |
|      << "\t\t<string>dSYM</string>\n"
 | |
|      << "\t\t<key>CFBundleSignature</key>\n"
 | |
|      << "\t\t<string>\?\?\?\?</string>\n";
 | |
| 
 | |
|   if (!BI.OmitShortVersion()) {
 | |
|     PL << "\t\t<key>CFBundleShortVersionString</key>\n";
 | |
|     PL << "\t\t<string>";
 | |
|     printHTMLEscaped(BI.ShortVersionStr, PL);
 | |
|     PL << "</string>\n";
 | |
|   }
 | |
| 
 | |
|   PL << "\t\t<key>CFBundleVersion</key>\n";
 | |
|   PL << "\t\t<string>";
 | |
|   printHTMLEscaped(BI.VersionStr, PL);
 | |
|   PL << "</string>\n";
 | |
| 
 | |
|   if (!Toolchain.empty()) {
 | |
|     PL << "\t\t<key>Toolchain</key>\n";
 | |
|     PL << "\t\t<string>";
 | |
|     printHTMLEscaped(Toolchain, PL);
 | |
|     PL << "</string>\n";
 | |
|   }
 | |
| 
 | |
|   PL << "\t</dict>\n"
 | |
|      << "</plist>\n";
 | |
| 
 | |
|   PL.close();
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| static bool createBundleDir(llvm::StringRef BundleBase) {
 | |
|   if (NoOutput)
 | |
|     return true;
 | |
| 
 | |
|   llvm::SmallString<128> Bundle(BundleBase);
 | |
|   llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF");
 | |
|   if (std::error_code EC = create_directories(Bundle.str(), true,
 | |
|                                               llvm::sys::fs::perms::all_all)) {
 | |
|     WithColor::error() << "cannot create directory " << Bundle << ": "
 | |
|                        << EC.message() << "\n";
 | |
|     return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) {
 | |
|   if (OutputFile == "-") {
 | |
|     WithColor::warning() << "verification skipped for " << Arch
 | |
|                          << "because writing to stdout.\n";
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);
 | |
|   if (!BinOrErr) {
 | |
|     errs() << OutputFile << ": " << toString(BinOrErr.takeError());
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   Binary &Binary = *BinOrErr.get().getBinary();
 | |
|   if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) {
 | |
|     raw_ostream &os = Verbose ? errs() : nulls();
 | |
|     os << "Verifying DWARF for architecture: " << Arch << "\n";
 | |
|     std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
 | |
|     DIDumpOptions DumpOpts;
 | |
|     bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion());
 | |
|     if (!success)
 | |
|       WithColor::error() << "verification failed for " << Arch << '\n';
 | |
|     return success;
 | |
|   }
 | |
| 
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| static std::string getOutputFileName(llvm::StringRef InputFile) {
 | |
|   // When updating, do in place replacement.
 | |
|   if (OutputFileOpt.empty() && Update)
 | |
|     return InputFile;
 | |
| 
 | |
|   // If a flat dSYM has been requested, things are pretty simple.
 | |
|   if (FlatOut) {
 | |
|     if (OutputFileOpt.empty()) {
 | |
|       if (InputFile == "-")
 | |
|         return "a.out.dwarf";
 | |
|       return (InputFile + ".dwarf").str();
 | |
|     }
 | |
| 
 | |
|     return OutputFileOpt;
 | |
|   }
 | |
| 
 | |
|   // We need to create/update a dSYM bundle.
 | |
|   // A bundle hierarchy looks like this:
 | |
|   //   <bundle name>.dSYM/
 | |
|   //       Contents/
 | |
|   //          Info.plist
 | |
|   //          Resources/
 | |
|   //             DWARF/
 | |
|   //                <DWARF file(s)>
 | |
|   std::string DwarfFile =
 | |
|       InputFile == "-" ? llvm::StringRef("a.out") : InputFile;
 | |
|   llvm::SmallString<128> BundleDir(OutputFileOpt);
 | |
|   if (BundleDir.empty())
 | |
|     BundleDir = DwarfFile + ".dSYM";
 | |
|   if (!createBundleDir(BundleDir) || !createPlistFile(DwarfFile, BundleDir))
 | |
|     return "";
 | |
| 
 | |
|   llvm::sys::path::append(BundleDir, "Contents", "Resources", "DWARF",
 | |
|                           llvm::sys::path::filename(DwarfFile));
 | |
|   return BundleDir.str();
 | |
| }
 | |
| 
 | |
| /// Parses the command line options into the LinkOptions struct and performs
 | |
| /// some sanity checking. Returns an error in case the latter fails.
 | |
| static Expected<LinkOptions> getOptions() {
 | |
|   LinkOptions Options;
 | |
| 
 | |
|   Options.Verbose = Verbose;
 | |
|   Options.NoOutput = NoOutput;
 | |
|   Options.NoODR = NoODR;
 | |
|   Options.Minimize = Minimize;
 | |
|   Options.Update = Update;
 | |
|   Options.NoTimestamp = NoTimestamp;
 | |
|   Options.PrependPath = OsoPrependPath;
 | |
|   Options.TheAccelTableKind = AcceleratorTable;
 | |
| 
 | |
|   if (Assembly)
 | |
|     Options.FileType = OutputFileType::Assembly;
 | |
| 
 | |
|   if (Options.Update && std::find(InputFiles.begin(), InputFiles.end(), "-") !=
 | |
|                             InputFiles.end()) {
 | |
|     // FIXME: We cannot use stdin for an update because stdin will be
 | |
|     // consumed by the BinaryHolder during the debugmap parsing, and
 | |
|     // then we will want to consume it again in DwarfLinker. If we
 | |
|     // used a unique BinaryHolder object that could cache multiple
 | |
|     // binaries this restriction would go away.
 | |
|     return make_error<StringError>(
 | |
|         "standard input cannot be used as input for a dSYM update.",
 | |
|         inconvertibleErrorCode());
 | |
|   }
 | |
| 
 | |
|   if (NumThreads == 0)
 | |
|     Options.Threads = llvm::thread::hardware_concurrency();
 | |
|   if (DumpDebugMap || Verbose)
 | |
|     Options.Threads = 1;
 | |
| 
 | |
|   return Options;
 | |
| }
 | |
| 
 | |
| /// Return a list of input files. This function has logic for dealing with the
 | |
| /// special case where we might have dSYM bundles as input. The function
 | |
| /// returns an error when the directory structure doesn't match that of a dSYM
 | |
| /// bundle.
 | |
| static Expected<std::vector<std::string>> getInputs(bool DsymAsInput) {
 | |
|   if (!DsymAsInput)
 | |
|     return InputFiles;
 | |
| 
 | |
|   // If we are updating, we might get dSYM bundles as input.
 | |
|   std::vector<std::string> Inputs;
 | |
|   for (const auto &Input : InputFiles) {
 | |
|     if (!llvm::sys::fs::is_directory(Input)) {
 | |
|       Inputs.push_back(Input);
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     // Make sure that we're dealing with a dSYM bundle.
 | |
|     SmallString<256> BundlePath(Input);
 | |
|     sys::path::append(BundlePath, "Contents", "Resources", "DWARF");
 | |
|     if (!llvm::sys::fs::is_directory(BundlePath))
 | |
|       return make_error<StringError>(
 | |
|           Input + " is a directory, but doesn't look like a dSYM bundle.",
 | |
|           inconvertibleErrorCode());
 | |
| 
 | |
|     // Create a directory iterator to iterate over all the entries in the
 | |
|     // bundle.
 | |
|     std::error_code EC;
 | |
|     llvm::sys::fs::directory_iterator DirIt(BundlePath, EC);
 | |
|     llvm::sys::fs::directory_iterator DirEnd;
 | |
|     if (EC)
 | |
|       return errorCodeToError(EC);
 | |
| 
 | |
|     // Add each entry to the list of inputs.
 | |
|     while (DirIt != DirEnd) {
 | |
|       Inputs.push_back(DirIt->path());
 | |
|       DirIt.increment(EC);
 | |
|       if (EC)
 | |
|         return errorCodeToError(EC);
 | |
|     }
 | |
|   }
 | |
|   return Inputs;
 | |
| }
 | |
| 
 | |
| int main(int argc, char **argv) {
 | |
|   InitLLVM X(argc, argv);
 | |
| 
 | |
|   void *P = (void *)(intptr_t)getOutputFileName;
 | |
|   std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], P);
 | |
|   SDKPath = llvm::sys::path::parent_path(SDKPath);
 | |
| 
 | |
|   HideUnrelatedOptions({&DsymCategory, &ColorCategory});
 | |
|   llvm::cl::ParseCommandLineOptions(
 | |
|       argc, argv,
 | |
|       "manipulate archived DWARF debug symbol files.\n\n"
 | |
|       "dsymutil links the DWARF debug information found in the object files\n"
 | |
|       "for the executable <input file> by using debug symbols information\n"
 | |
|       "contained in its symbol table.\n");
 | |
| 
 | |
|   if (Help) {
 | |
|     PrintHelpMessage();
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   if (Version) {
 | |
|     llvm::cl::PrintVersionMessage();
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   auto OptionsOrErr = getOptions();
 | |
|   if (!OptionsOrErr) {
 | |
|     WithColor::error() << toString(OptionsOrErr.takeError());
 | |
|     return 1;
 | |
|   }
 | |
| 
 | |
|   llvm::InitializeAllTargetInfos();
 | |
|   llvm::InitializeAllTargetMCs();
 | |
|   llvm::InitializeAllTargets();
 | |
|   llvm::InitializeAllAsmPrinters();
 | |
| 
 | |
|   auto InputsOrErr = getInputs(OptionsOrErr->Update);
 | |
|   if (!InputsOrErr) {
 | |
|     WithColor::error() << toString(InputsOrErr.takeError()) << '\n';
 | |
|     return 1;
 | |
|   }
 | |
| 
 | |
|   if (!FlatOut && OutputFileOpt == "-") {
 | |
|     WithColor::error() << "cannot emit to standard output without --flat\n";
 | |
|     return 1;
 | |
|   }
 | |
| 
 | |
|   if (InputsOrErr->size() > 1 && FlatOut && !OutputFileOpt.empty()) {
 | |
|     WithColor::error() << "cannot use -o with multiple inputs in flat mode\n";
 | |
|     return 1;
 | |
|   }
 | |
| 
 | |
|   if (getenv("RC_DEBUG_OPTIONS"))
 | |
|     PaperTrailWarnings = true;
 | |
| 
 | |
|   if (PaperTrailWarnings && InputIsYAMLDebugMap)
 | |
|     WithColor::warning()
 | |
|         << "Paper trail warnings are not supported for YAML input";
 | |
| 
 | |
|   for (const auto &Arch : ArchFlags)
 | |
|     if (Arch != "*" && Arch != "all" &&
 | |
|         !llvm::object::MachOObjectFile::isValidArch(Arch)) {
 | |
|       WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n";
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|   for (auto &InputFile : *InputsOrErr) {
 | |
|     // Dump the symbol table for each input file and requested arch
 | |
|     if (DumpStab) {
 | |
|       if (!dumpStab(InputFile, ArchFlags, OsoPrependPath))
 | |
|         return 1;
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     auto DebugMapPtrsOrErr =
 | |
|         parseDebugMap(InputFile, ArchFlags, OsoPrependPath, PaperTrailWarnings,
 | |
|                       Verbose, InputIsYAMLDebugMap);
 | |
| 
 | |
|     if (auto EC = DebugMapPtrsOrErr.getError()) {
 | |
|       WithColor::error() << "cannot parse the debug map for '" << InputFile
 | |
|                          << "': " << EC.message() << '\n';
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|     if (OptionsOrErr->Update) {
 | |
|       // The debug map should be empty. Add one object file corresponding to
 | |
|       // the input file.
 | |
|       for (auto &Map : *DebugMapPtrsOrErr)
 | |
|         Map->addDebugMapObject(InputFile,
 | |
|                                llvm::sys::TimePoint<std::chrono::seconds>());
 | |
|     }
 | |
| 
 | |
|     // Ensure that the debug map is not empty (anymore).
 | |
|     if (DebugMapPtrsOrErr->empty()) {
 | |
|       WithColor::error() << "no architecture to link\n";
 | |
|       return 1;
 | |
|     }
 | |
| 
 | |
|     // Shared a single binary holder for all the link steps.
 | |
|     BinaryHolder BinHolder;
 | |
| 
 | |
|     NumThreads =
 | |
|         std::min<unsigned>(OptionsOrErr->Threads, DebugMapPtrsOrErr->size());
 | |
|     llvm::ThreadPool Threads(NumThreads);
 | |
| 
 | |
|     // If there is more than one link to execute, we need to generate
 | |
|     // temporary files.
 | |
|     bool NeedsTempFiles =
 | |
|         !DumpDebugMap && (OutputFileOpt != "-") &&
 | |
|         (DebugMapPtrsOrErr->size() != 1 || OptionsOrErr->Update);
 | |
| 
 | |
|     llvm::SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
 | |
|     std::atomic_char AllOK(1);
 | |
|     for (auto &Map : *DebugMapPtrsOrErr) {
 | |
|       if (Verbose || DumpDebugMap)
 | |
|         Map->print(llvm::outs());
 | |
| 
 | |
|       if (DumpDebugMap)
 | |
|         continue;
 | |
| 
 | |
|       if (Map->begin() == Map->end())
 | |
|         WithColor::warning()
 | |
|             << "no debug symbols in executable (-arch "
 | |
|             << MachOUtils::getArchName(Map->getTriple().getArchName()) << ")\n";
 | |
| 
 | |
|       // Using a std::shared_ptr rather than std::unique_ptr because move-only
 | |
|       // types don't work with std::bind in the ThreadPool implementation.
 | |
|       std::shared_ptr<raw_fd_ostream> OS;
 | |
|       std::string OutputFile = getOutputFileName(InputFile);
 | |
|       if (NeedsTempFiles) {
 | |
|         TempFiles.emplace_back(Map->getTriple().getArchName().str());
 | |
| 
 | |
|         auto E = TempFiles.back().createTempFile();
 | |
|         if (E) {
 | |
|           errs() << toString(std::move(E));
 | |
|           return 1;
 | |
|         }
 | |
| 
 | |
|         auto &TempFile = *(TempFiles.back().File);
 | |
|         OS = std::make_shared<raw_fd_ostream>(TempFile.FD,
 | |
|                                               /*shouldClose*/ false);
 | |
|         OutputFile = TempFile.TmpName;
 | |
|       } else {
 | |
|         std::error_code EC;
 | |
|         OS = std::make_shared<raw_fd_ostream>(NoOutput ? "-" : OutputFile, EC,
 | |
|                                               sys::fs::F_None);
 | |
|         if (EC) {
 | |
|           errs() << OutputFile << ": " << EC.message();
 | |
|           return 1;
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       auto LinkLambda = [&,
 | |
|                          OutputFile](std::shared_ptr<raw_fd_ostream> Stream) {
 | |
|         AllOK.fetch_and(linkDwarf(*Stream, BinHolder, *Map, *OptionsOrErr));
 | |
|         Stream->flush();
 | |
|         if (Verify && !NoOutput)
 | |
|           AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName()));
 | |
|       };
 | |
| 
 | |
|       // FIXME: The DwarfLinker can have some very deep recursion that can max
 | |
|       // out the (significantly smaller) stack when using threads. We don't
 | |
|       // want this limitation when we only have a single thread.
 | |
|       if (NumThreads == 1)
 | |
|         LinkLambda(OS);
 | |
|       else
 | |
|         Threads.async(LinkLambda, OS);
 | |
|     }
 | |
| 
 | |
|     Threads.wait();
 | |
| 
 | |
|     if (!AllOK)
 | |
|       return 1;
 | |
| 
 | |
|     if (NeedsTempFiles &&
 | |
|         !MachOUtils::generateUniversalBinary(
 | |
|             TempFiles, getOutputFileName(InputFile), *OptionsOrErr, SDKPath))
 | |
|       return 1;
 | |
|   }
 | |
| 
 | |
|   return 0;
 | |
| }
 |