dsymutil.cpp 23 KB

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