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