[MIParser] Set RegClassOrRegBank during instruction parsing
[llvm-complete.git] / tools / dsymutil / dsymutil.cpp
blob88a85937a627ca35a385325e2e8902b81170b903
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 //===----------------------------------------------------------------------===//
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 "llvm/ADT/SmallString.h"
20 #include "llvm/ADT/SmallVector.h"
21 #include "llvm/ADT/StringExtras.h"
22 #include "llvm/ADT/StringRef.h"
23 #include "llvm/ADT/StringSwitch.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/FileSystem.h"
35 #include "llvm/Support/InitLLVM.h"
36 #include "llvm/Support/ManagedStatic.h"
37 #include "llvm/Support/Path.h"
38 #include "llvm/Support/TargetSelect.h"
39 #include "llvm/Support/ThreadPool.h"
40 #include "llvm/Support/WithColor.h"
41 #include "llvm/Support/raw_ostream.h"
42 #include "llvm/Support/thread.h"
43 #include <algorithm>
44 #include <cstdint>
45 #include <cstdlib>
46 #include <string>
47 #include <system_error>
49 using namespace llvm;
50 using namespace llvm::dsymutil;
51 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
63 #define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE;
64 #include "Options.inc"
65 #undef PREFIX
67 const opt::OptTable::Info InfoTable[] = {
68 #define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \
69 HELPTEXT, METAVAR, VALUES) \
70 { \
71 PREFIX, NAME, HELPTEXT, \
72 METAVAR, OPT_##ID, opt::Option::KIND##Class, \
73 PARAM, FLAGS, OPT_##GROUP, \
74 OPT_##ALIAS, ALIASARGS, VALUES},
75 #include "Options.inc"
76 #undef OPTION
79 class DsymutilOptTable : public opt::OptTable {
80 public:
81 DsymutilOptTable() : OptTable(InfoTable) {}
83 } // namespace
85 struct DsymutilOptions {
86 bool DumpDebugMap = false;
87 bool DumpStab = false;
88 bool Flat = false;
89 bool InputIsYAMLDebugMap = false;
90 bool PaperTrailWarnings = false;
91 bool Verify = false;
92 std::string SymbolMap;
93 std::string OutputFile;
94 std::string Toolchain;
95 std::vector<std::string> Archs;
96 std::vector<std::string> InputFiles;
97 unsigned NumThreads;
98 dsymutil::LinkOptions LinkOpts;
101 /// Return a list of input files. This function has logic for dealing with the
102 /// special case where we might have dSYM bundles as input. The function
103 /// returns an error when the directory structure doesn't match that of a dSYM
104 /// bundle.
105 static Expected<std::vector<std::string>> getInputs(opt::InputArgList &Args,
106 bool DsymAsInput) {
107 std::vector<std::string> InputFiles;
108 for (auto *File : Args.filtered(OPT_INPUT))
109 InputFiles.push_back(File->getValue());
111 if (!DsymAsInput)
112 return InputFiles;
114 // If we are updating, we might get dSYM bundles as input.
115 std::vector<std::string> Inputs;
116 for (const auto &Input : InputFiles) {
117 if (!sys::fs::is_directory(Input)) {
118 Inputs.push_back(Input);
119 continue;
122 // Make sure that we're dealing with a dSYM bundle.
123 SmallString<256> BundlePath(Input);
124 sys::path::append(BundlePath, "Contents", "Resources", "DWARF");
125 if (!sys::fs::is_directory(BundlePath))
126 return make_error<StringError>(
127 Input + " is a directory, but doesn't look like a dSYM bundle.",
128 inconvertibleErrorCode());
130 // Create a directory iterator to iterate over all the entries in the
131 // bundle.
132 std::error_code EC;
133 sys::fs::directory_iterator DirIt(BundlePath, EC);
134 sys::fs::directory_iterator DirEnd;
135 if (EC)
136 return errorCodeToError(EC);
138 // Add each entry to the list of inputs.
139 while (DirIt != DirEnd) {
140 Inputs.push_back(DirIt->path());
141 DirIt.increment(EC);
142 if (EC)
143 return errorCodeToError(EC);
146 return Inputs;
149 // Verify that the given combination of options makes sense.
150 static Error verifyOptions(const DsymutilOptions &Options) {
151 if (Options.InputFiles.empty()) {
152 return make_error<StringError>("no input files specified",
153 errc::invalid_argument);
156 if (Options.LinkOpts.Update &&
157 std::find(Options.InputFiles.begin(), Options.InputFiles.end(), "-") !=
158 Options.InputFiles.end()) {
159 // FIXME: We cannot use stdin for an update because stdin will be
160 // consumed by the BinaryHolder during the debugmap parsing, and
161 // then we will want to consume it again in DwarfLinker. If we
162 // used a unique BinaryHolder object that could cache multiple
163 // binaries this restriction would go away.
164 return make_error<StringError>(
165 "standard input cannot be used as input for a dSYM update.",
166 errc::invalid_argument);
169 if (!Options.Flat && Options.OutputFile == "-")
170 return make_error<StringError>(
171 "cannot emit to standard output without --flat.",
172 errc::invalid_argument);
174 if (Options.InputFiles.size() > 1 && Options.Flat &&
175 !Options.OutputFile.empty())
176 return make_error<StringError>(
177 "cannot use -o with multiple inputs in flat mode.",
178 errc::invalid_argument);
180 if (Options.PaperTrailWarnings && Options.InputIsYAMLDebugMap)
181 return make_error<StringError>(
182 "paper trail warnings are not supported for YAML input.",
183 errc::invalid_argument);
185 return Error::success();
188 static Expected<AccelTableKind> getAccelTableKind(opt::InputArgList &Args) {
189 if (opt::Arg *Accelerator = Args.getLastArg(OPT_accelerator)) {
190 StringRef S = Accelerator->getValue();
191 if (S == "Apple")
192 return AccelTableKind::Apple;
193 if (S == "Dwarf")
194 return AccelTableKind::Dwarf;
195 if (S == "Default")
196 return AccelTableKind::Default;
197 return make_error<StringError>(
198 "invalid accelerator type specified: '" + S +
199 "'. Support values are 'Apple', 'Dwarf' and 'Default'.",
200 inconvertibleErrorCode());
202 return AccelTableKind::Default;
205 /// Parses the command line options into the LinkOptions struct and performs
206 /// some sanity checking. Returns an error in case the latter fails.
207 static Expected<DsymutilOptions> getOptions(opt::InputArgList &Args) {
208 DsymutilOptions Options;
210 Options.DumpDebugMap = Args.hasArg(OPT_dump_debug_map);
211 Options.DumpStab = Args.hasArg(OPT_symtab);
212 Options.Flat = Args.hasArg(OPT_flat);
213 Options.InputIsYAMLDebugMap = Args.hasArg(OPT_yaml_input);
214 Options.PaperTrailWarnings = Args.hasArg(OPT_papertrail);
215 Options.Verify = Args.hasArg(OPT_verify);
217 Options.LinkOpts.Minimize = Args.hasArg(OPT_minimize);
218 Options.LinkOpts.NoODR = Args.hasArg(OPT_no_odr);
219 Options.LinkOpts.NoOutput = Args.hasArg(OPT_no_output);
220 Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp);
221 Options.LinkOpts.Update = Args.hasArg(OPT_update);
222 Options.LinkOpts.Verbose = Args.hasArg(OPT_verbose);
224 if (Expected<AccelTableKind> AccelKind = getAccelTableKind(Args)) {
225 Options.LinkOpts.TheAccelTableKind = *AccelKind;
226 } else {
227 return AccelKind.takeError();
230 if (opt::Arg *SymbolMap = Args.getLastArg(OPT_symbolmap))
231 Options.SymbolMap = SymbolMap->getValue();
233 if (Args.hasArg(OPT_symbolmap))
234 Options.LinkOpts.Update = true;
236 if (Expected<std::vector<std::string>> InputFiles =
237 getInputs(Args, Options.LinkOpts.Update)) {
238 Options.InputFiles = std::move(*InputFiles);
239 } else {
240 return InputFiles.takeError();
243 for (auto *Arch : Args.filtered(OPT_arch))
244 Options.Archs.push_back(Arch->getValue());
246 if (opt::Arg *OsoPrependPath = Args.getLastArg(OPT_oso_prepend_path))
247 Options.LinkOpts.PrependPath = OsoPrependPath->getValue();
249 if (opt::Arg *OutputFile = Args.getLastArg(OPT_output))
250 Options.OutputFile = OutputFile->getValue();
252 if (opt::Arg *Toolchain = Args.getLastArg(OPT_toolchain))
253 Options.Toolchain = Toolchain->getValue();
255 if (Args.hasArg(OPT_assembly))
256 Options.LinkOpts.FileType = OutputFileType::Assembly;
258 if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads))
259 Options.LinkOpts.Threads = atoi(NumThreads->getValue());
260 else
261 Options.LinkOpts.Threads = thread::hardware_concurrency();
263 if (Options.DumpDebugMap || Options.LinkOpts.Verbose)
264 Options.LinkOpts.Threads = 1;
266 if (getenv("RC_DEBUG_OPTIONS"))
267 Options.PaperTrailWarnings = true;
269 if (Error E = verifyOptions(Options))
270 return std::move(E);
271 return Options;
274 static Error createPlistFile(StringRef Bin, StringRef BundleRoot,
275 StringRef Toolchain) {
276 // Create plist file to write to.
277 SmallString<128> InfoPlist(BundleRoot);
278 sys::path::append(InfoPlist, "Contents/Info.plist");
279 std::error_code EC;
280 raw_fd_ostream PL(InfoPlist, EC, sys::fs::OF_Text);
281 if (EC)
282 return make_error<StringError>(
283 "cannot create Plist: " + toString(errorCodeToError(EC)), EC);
285 CFBundleInfo BI = getBundleInfo(Bin);
287 if (BI.IDStr.empty()) {
288 StringRef BundleID = *sys::path::rbegin(BundleRoot);
289 if (sys::path::extension(BundleRoot) == ".dSYM")
290 BI.IDStr = sys::path::stem(BundleID);
291 else
292 BI.IDStr = BundleID;
295 // Print out information to the plist file.
296 PL << "<?xml version=\"1.0\" encoding=\"UTF-8\"\?>\n"
297 << "<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\" "
298 << "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
299 << "<plist version=\"1.0\">\n"
300 << "\t<dict>\n"
301 << "\t\t<key>CFBundleDevelopmentRegion</key>\n"
302 << "\t\t<string>English</string>\n"
303 << "\t\t<key>CFBundleIdentifier</key>\n"
304 << "\t\t<string>com.apple.xcode.dsym." << BI.IDStr << "</string>\n"
305 << "\t\t<key>CFBundleInfoDictionaryVersion</key>\n"
306 << "\t\t<string>6.0</string>\n"
307 << "\t\t<key>CFBundlePackageType</key>\n"
308 << "\t\t<string>dSYM</string>\n"
309 << "\t\t<key>CFBundleSignature</key>\n"
310 << "\t\t<string>\?\?\?\?</string>\n";
312 if (!BI.OmitShortVersion()) {
313 PL << "\t\t<key>CFBundleShortVersionString</key>\n";
314 PL << "\t\t<string>";
315 printHTMLEscaped(BI.ShortVersionStr, PL);
316 PL << "</string>\n";
319 PL << "\t\t<key>CFBundleVersion</key>\n";
320 PL << "\t\t<string>";
321 printHTMLEscaped(BI.VersionStr, PL);
322 PL << "</string>\n";
324 if (!Toolchain.empty()) {
325 PL << "\t\t<key>Toolchain</key>\n";
326 PL << "\t\t<string>";
327 printHTMLEscaped(Toolchain, PL);
328 PL << "</string>\n";
331 PL << "\t</dict>\n"
332 << "</plist>\n";
334 PL.close();
335 return Error::success();
338 static Error createBundleDir(StringRef BundleBase) {
339 SmallString<128> Bundle(BundleBase);
340 sys::path::append(Bundle, "Contents", "Resources", "DWARF");
341 if (std::error_code EC =
342 create_directories(Bundle.str(), true, sys::fs::perms::all_all))
343 return make_error<StringError>(
344 "cannot create bundle: " + toString(errorCodeToError(EC)), EC);
346 return Error::success();
349 static bool verify(StringRef OutputFile, StringRef Arch, bool Verbose) {
350 if (OutputFile == "-") {
351 WithColor::warning() << "verification skipped for " << Arch
352 << "because writing to stdout.\n";
353 return true;
356 Expected<OwningBinary<Binary>> BinOrErr = createBinary(OutputFile);
357 if (!BinOrErr) {
358 WithColor::error() << OutputFile << ": " << toString(BinOrErr.takeError());
359 return false;
362 Binary &Binary = *BinOrErr.get().getBinary();
363 if (auto *Obj = dyn_cast<MachOObjectFile>(&Binary)) {
364 raw_ostream &os = Verbose ? errs() : nulls();
365 os << "Verifying DWARF for architecture: " << Arch << "\n";
366 std::unique_ptr<DWARFContext> DICtx = DWARFContext::create(*Obj);
367 DIDumpOptions DumpOpts;
368 bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion());
369 if (!success)
370 WithColor::error() << "verification failed for " << Arch << '\n';
371 return success;
374 return false;
377 namespace {
378 struct OutputLocation {
379 OutputLocation(std::string DWARFFile, Optional<std::string> ResourceDir = {})
380 : DWARFFile(DWARFFile), ResourceDir(ResourceDir) {}
381 /// This method is a workaround for older compilers.
382 Optional<std::string> getResourceDir() const { return ResourceDir; }
383 std::string DWARFFile;
384 Optional<std::string> ResourceDir;
386 } // namespace
388 static Expected<OutputLocation>
389 getOutputFileName(StringRef InputFile, const DsymutilOptions &Options) {
390 if (Options.OutputFile == "-")
391 return OutputLocation(Options.OutputFile);
393 // When updating, do in place replacement.
394 if (Options.OutputFile.empty() &&
395 (Options.LinkOpts.Update || !Options.SymbolMap.empty()))
396 return OutputLocation(InputFile);
398 // If a flat dSYM has been requested, things are pretty simple.
399 if (Options.Flat) {
400 if (Options.OutputFile.empty()) {
401 if (InputFile == "-")
402 return OutputLocation{"a.out.dwarf", {}};
403 return OutputLocation((InputFile + ".dwarf").str());
406 return OutputLocation(Options.OutputFile);
409 // We need to create/update a dSYM bundle.
410 // A bundle hierarchy looks like this:
411 // <bundle name>.dSYM/
412 // Contents/
413 // Info.plist
414 // Resources/
415 // DWARF/
416 // <DWARF file(s)>
417 std::string DwarfFile = InputFile == "-" ? StringRef("a.out") : InputFile;
418 SmallString<128> Path(Options.OutputFile);
419 if (Path.empty())
420 Path = DwarfFile + ".dSYM";
421 if (!Options.LinkOpts.NoOutput) {
422 if (auto E = createBundleDir(Path))
423 return std::move(E);
424 if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain))
425 return std::move(E);
428 sys::path::append(Path, "Contents", "Resources");
429 std::string ResourceDir = Path.str();
430 sys::path::append(Path, "DWARF", sys::path::filename(DwarfFile));
431 return OutputLocation(Path.str(), ResourceDir);
434 int main(int argc, char **argv) {
435 InitLLVM X(argc, argv);
437 // Parse arguments.
438 DsymutilOptTable T;
439 unsigned MAI;
440 unsigned MAC;
441 ArrayRef<const char *> ArgsArr = makeArrayRef(argv + 1, argc - 1);
442 opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC);
444 void *P = (void *)(intptr_t)getOutputFileName;
445 std::string SDKPath = sys::fs::getMainExecutable(argv[0], P);
446 SDKPath = sys::path::parent_path(SDKPath);
448 for (auto *Arg : Args.filtered(OPT_UNKNOWN)) {
449 WithColor::warning() << "ignoring unknown option: " << Arg->getSpelling()
450 << '\n';
453 if (Args.hasArg(OPT_help)) {
454 T.PrintHelp(
455 outs(), (std::string(argv[0]) + " [options] <input files>").c_str(),
456 "manipulate archived DWARF debug symbol files.\n\n"
457 "dsymutil links the DWARF debug information found in the object files\n"
458 "for the executable <input file> by using debug symbols information\n"
459 "contained in its symbol table.\n",
460 false);
461 return 0;
464 if (Args.hasArg(OPT_version)) {
465 cl::PrintVersionMessage();
466 return 0;
469 auto OptionsOrErr = getOptions(Args);
470 if (!OptionsOrErr) {
471 WithColor::error() << toString(OptionsOrErr.takeError());
472 return 1;
475 auto &Options = *OptionsOrErr;
477 InitializeAllTargetInfos();
478 InitializeAllTargetMCs();
479 InitializeAllTargets();
480 InitializeAllAsmPrinters();
482 for (const auto &Arch : Options.Archs)
483 if (Arch != "*" && Arch != "all" &&
484 !object::MachOObjectFile::isValidArch(Arch)) {
485 WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n";
486 return 1;
489 SymbolMapLoader SymMapLoader(Options.SymbolMap);
491 for (auto &InputFile : Options.InputFiles) {
492 // Dump the symbol table for each input file and requested arch
493 if (Options.DumpStab) {
494 if (!dumpStab(InputFile, Options.Archs, Options.LinkOpts.PrependPath))
495 return 1;
496 continue;
499 auto DebugMapPtrsOrErr =
500 parseDebugMap(InputFile, Options.Archs, Options.LinkOpts.PrependPath,
501 Options.PaperTrailWarnings, Options.LinkOpts.Verbose,
502 Options.InputIsYAMLDebugMap);
504 if (auto EC = DebugMapPtrsOrErr.getError()) {
505 WithColor::error() << "cannot parse the debug map for '" << InputFile
506 << "': " << EC.message() << '\n';
507 return 1;
510 if (Options.LinkOpts.Update) {
511 // The debug map should be empty. Add one object file corresponding to
512 // the input file.
513 for (auto &Map : *DebugMapPtrsOrErr)
514 Map->addDebugMapObject(InputFile,
515 sys::TimePoint<std::chrono::seconds>());
518 // Ensure that the debug map is not empty (anymore).
519 if (DebugMapPtrsOrErr->empty()) {
520 WithColor::error() << "no architecture to link\n";
521 return 1;
524 // Shared a single binary holder for all the link steps.
525 BinaryHolder BinHolder;
527 unsigned ThreadCount =
528 std::min<unsigned>(Options.LinkOpts.Threads, DebugMapPtrsOrErr->size());
529 ThreadPool Threads(ThreadCount);
531 // If there is more than one link to execute, we need to generate
532 // temporary files.
533 const bool NeedsTempFiles =
534 !Options.DumpDebugMap && (Options.OutputFile != "-") &&
535 (DebugMapPtrsOrErr->size() != 1 || Options.LinkOpts.Update);
536 const bool Verify = Options.Verify && !Options.LinkOpts.NoOutput;
538 SmallVector<MachOUtils::ArchAndFile, 4> TempFiles;
539 std::atomic_char AllOK(1);
540 for (auto &Map : *DebugMapPtrsOrErr) {
541 if (Options.LinkOpts.Verbose || Options.DumpDebugMap)
542 Map->print(outs());
544 if (Options.DumpDebugMap)
545 continue;
547 if (!Options.SymbolMap.empty())
548 Options.LinkOpts.Translator = SymMapLoader.Load(InputFile, *Map);
550 if (Map->begin() == Map->end())
551 WithColor::warning()
552 << "no debug symbols in executable (-arch "
553 << MachOUtils::getArchName(Map->getTriple().getArchName()) << ")\n";
555 // Using a std::shared_ptr rather than std::unique_ptr because move-only
556 // types don't work with std::bind in the ThreadPool implementation.
557 std::shared_ptr<raw_fd_ostream> OS;
559 Expected<OutputLocation> OutputLocationOrErr =
560 getOutputFileName(InputFile, Options);
561 if (!OutputLocationOrErr) {
562 WithColor::error() << toString(OutputLocationOrErr.takeError());
563 return 1;
565 Options.LinkOpts.ResourceDir = OutputLocationOrErr->getResourceDir();
567 std::string OutputFile = OutputLocationOrErr->DWARFFile;
568 if (NeedsTempFiles) {
569 TempFiles.emplace_back(Map->getTriple().getArchName().str());
571 auto E = TempFiles.back().createTempFile();
572 if (E) {
573 WithColor::error() << toString(std::move(E));
574 return 1;
577 auto &TempFile = *(TempFiles.back().File);
578 OS = std::make_shared<raw_fd_ostream>(TempFile.FD,
579 /*shouldClose*/ false);
580 OutputFile = TempFile.TmpName;
581 } else {
582 std::error_code EC;
583 OS = std::make_shared<raw_fd_ostream>(
584 Options.LinkOpts.NoOutput ? "-" : OutputFile, EC, sys::fs::OF_None);
585 if (EC) {
586 WithColor::error() << OutputFile << ": " << EC.message();
587 return 1;
591 auto LinkLambda = [&, OutputFile](std::shared_ptr<raw_fd_ostream> Stream,
592 LinkOptions Options) {
593 AllOK.fetch_and(
594 linkDwarf(*Stream, BinHolder, *Map, std::move(Options)));
595 Stream->flush();
596 if (Verify)
597 AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName(),
598 Options.Verbose));
601 // FIXME: The DwarfLinker can have some very deep recursion that can max
602 // out the (significantly smaller) stack when using threads. We don't
603 // want this limitation when we only have a single thread.
604 if (ThreadCount == 1)
605 LinkLambda(OS, Options.LinkOpts);
606 else
607 Threads.async(LinkLambda, OS, Options.LinkOpts);
610 Threads.wait();
612 if (!AllOK)
613 return 1;
615 if (NeedsTempFiles) {
616 Expected<OutputLocation> OutputLocationOrErr =
617 getOutputFileName(InputFile, Options);
618 if (!OutputLocationOrErr) {
619 WithColor::error() << toString(OutputLocationOrErr.takeError());
620 return 1;
622 if (!MachOUtils::generateUniversalBinary(TempFiles,
623 OutputLocationOrErr->DWARFFile,
624 Options.LinkOpts, SDKPath))
625 return 1;
629 return 0;