dsymutil.cpp 28 KB

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