Index: llvm/trunk/test/tools/dsymutil/cmdline.test =================================================================== --- llvm/trunk/test/tools/dsymutil/cmdline.test +++ llvm/trunk/test/tools/dsymutil/cmdline.test @@ -1,21 +1,19 @@ RUN: dsymutil -help 2>&1 | FileCheck --check-prefix=HELP %s HELP: OVERVIEW: manipulate archived DWARF debug symbol files. -HELP: USAGE: dsymutil{{[^ ]*}} [options] +HELP: USAGE: {{.*}}dsymutil{{[^ ]*}} [options] HELP-NOT: -reverse-iterate -HELP: Color Options -HELP: -color -HELP: Specific Options: +HELP: Dsymutil Options: HELP: -accelerator -HELP: -arch= +HELP: -arch HELP: -dump-debug-map HELP: -flat HELP: -minimize HELP: -no-odr HELP: -no-output HELP: -no-swiftmodule-timestamp -HELP: -num-threads= -HELP: -o= -HELP: -oso-prepend-path= +HELP: -num-threads +HELP: -oso-prepend-path +HELP: -o HELP: -papertrail HELP: -symbol-map HELP: -symtab Index: llvm/trunk/tools/dsymutil/CMakeLists.txt =================================================================== --- llvm/trunk/tools/dsymutil/CMakeLists.txt +++ llvm/trunk/tools/dsymutil/CMakeLists.txt @@ -1,3 +1,7 @@ +set(LLVM_TARGET_DEFINITIONS Options.td) +tablegen(LLVM Options.inc -gen-opt-parser-defs) +add_public_tablegen_target(DsymutilTableGen) + set(LLVM_LINK_COMPONENTS AllTargetsAsmPrinters AllTargetsCodeGens @@ -7,6 +11,7 @@ DebugInfoDWARF MC Object + Option Support Target ) @@ -27,6 +32,7 @@ DEPENDS intrinsics_gen + ${tablegen_deps} ) if(APPLE) Index: llvm/trunk/tools/dsymutil/Options.td =================================================================== --- llvm/trunk/tools/dsymutil/Options.td +++ llvm/trunk/tools/dsymutil/Options.td @@ -0,0 +1,146 @@ +include "llvm/Option/OptParser.td" + +class F: Flag<["--", "-"], name>; + +def grp_general : OptionGroup<"Dsymutil">, HelpText<"Dsymutil Options">; + +def help: F<"help">, + HelpText<"Prints this help output.">, + Group; +def: Flag<["-"], "h">, + Alias, + HelpText<"Alias for --help">, + Group; + +def version: F<"version">, + HelpText<"Prints the dsymutil version.">, + Group; +def: Flag<["-"], "v">, + Alias, + HelpText<"Alias for --version">, + Group; + +def verbose: F<"verbose">, + HelpText<"Enable verbose mode.">, + Group; + +def verify: F<"verify">, + HelpText<"Run the DWARF verifier on the linked DWARF debug info.">, + Group; + +def no_output: F<"no-output">, + HelpText<"Do the link in memory, but do not emit the result file.">, + Group; + +def no_swiftmodule_timestamp: F<"no-swiftmodule-timestamp">, + HelpText<"Don't check timestamp for swiftmodule files.">, + Group; + +def no_odr: F<"no-odr">, + HelpText<"Do not use ODR (One Definition Rule) for type uniquing.">, + Group; + +def dump_debug_map: F<"dump-debug-map">, + HelpText<"Parse and dump the debug map to standard output. Not DWARF link will take place.">, + Group; + +def yaml_input: F<"y">, + HelpText<"Treat the input file is a YAML debug map rather than a binary.">, + Group; + +def papertrail: F<"papertrail">, + HelpText<"Embed warnings in the linked DWARF debug info.">, + Group; + +def assembly: F<"S">, + HelpText<"Output textual assembly instead of a binary dSYM companion file.">, + Group; + +def symtab: F<"symtab">, + HelpText<"Dumps the symbol table found in executable or object file(s) and exits.">, + Group; +def: Flag<["-"], "s">, + Alias, + HelpText<"Alias for --symtab">, + Group; + +def flat: F<"flat">, + HelpText<"Produce a flat dSYM file (not a bundle).">, + Group; +def: Flag<["-"], "f">, + Alias, + HelpText<"Alias for --flat">, + Group; + +def minimize: F<"minimize">, + HelpText<"When used when creating a dSYM file with Apple accelerator tables, " + "this option will suppress the emission of the .debug_inlines, " + ".debug_pubnames, and .debug_pubtypes sections since dsymutil " + "has better equivalents: .apple_names and .apple_types. When used in " + "conjunction with --update option, this option will cause redundant " + "accelerator tables to be removed.">, + Group; +def: Flag<["-"], "z">, + Alias, + HelpText<"Alias for --minimize">, + Group; + +def update: F<"update">, + HelpText<"Updates existing dSYM files to contain the latest accelerator tables and other DWARF optimizations.">, + Group; +def: Flag<["-"], "u">, + Alias, + HelpText<"Alias for --update">, + Group; + +def output: Separate<["--", "-"], "o">, + MetaVarName<"">, + HelpText<"Specify the output file. Defaults to .dwarf">, + Group; +def: Separate<["-"], "out">, + Alias, + HelpText<"Alias for --o">, + Group; +def: Joined<["--", "-"], "out=">, Alias; +def: Joined<["--", "-"], "o=">, Alias; + +def oso_prepend_path: Separate<["--", "-"], "oso-prepend-path">, + MetaVarName<"">, + HelpText<"Specify a directory to prepend to the paths of object files.">, + Group; +def: Joined<["--", "-"], "oso-prepend-path=">, Alias; + +def symbolmap: Separate<["--", "-"], "symbol-map">, + MetaVarName<"">, + HelpText<"Updates the existing dSYMs inplace using symbol map specified.">, + Group; +def: Joined<["--", "-"], "symbol-map=">, Alias; + +def arch: Separate<["--", "-"], "arch">, + MetaVarName<"">, + HelpText<"Link DWARF debug information only for specified CPU architecture" + "types. This option can be specified multiple times, once for each" + "desired architecture. All CPU architectures will be linked by" + "default.">, + Group; +def: Joined<["--", "-"], "arch=">, Alias; + +def accelerator: Separate<["--", "-"], "accelerator">, + MetaVarName<"">, + HelpText<"Specify the desired type of accelerator table. Valid options are 'Apple', 'Dwarf' and 'Default'">, + Group; +def: Joined<["--", "-"], "accelerator=">, Alias; + +def toolchain: Separate<["--", "-"], "toolchain">, + MetaVarName<"">, + HelpText<"Embed toolchain information in dSYM bundle.">, + Group; + +def threads: Separate<["--", "-"], "num-threads">, + MetaVarName<"">, + HelpText<"Specifies the maximum number of simultaneous threads to use when linking multiple architectures.">, + Group; +def: Separate<["-"], "j">, + Alias, + HelpText<"Alias for --num-threads">, + Group; Index: llvm/trunk/tools/dsymutil/dsymutil.cpp =================================================================== --- llvm/trunk/tools/dsymutil/dsymutil.cpp +++ llvm/trunk/tools/dsymutil/dsymutil.cpp @@ -20,12 +20,16 @@ #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" +#include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/DebugInfo/DIContext.h" #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/DWARF/DWARFVerifier.h" #include "llvm/Object/Binary.h" #include "llvm/Object/MachO.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/Option.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/InitLLVM.h" @@ -43,137 +47,227 @@ #include using namespace llvm; -using namespace llvm::cl; using namespace llvm::dsymutil; using namespace object; -static OptionCategory DsymCategory("Specific Options"); -static opt Help("h", desc("Alias for -help"), Hidden); -static opt Version("v", desc("Alias for -version"), Hidden); - -static list InputFiles(Positional, OneOrMore, - desc(""), cat(DsymCategory)); - -static opt - OutputFileOpt("o", - desc("Specify the output file. default: .dwarf"), - value_desc("filename"), cat(DsymCategory)); -static alias OutputFileOptA("out", desc("Alias for -o"), - aliasopt(OutputFileOpt)); - -static opt OsoPrependPath( - "oso-prepend-path", - desc("Specify a directory to prepend to the paths of object files."), - value_desc("path"), cat(DsymCategory)); - -static opt Assembly( - "S", - desc("Output textual assembly instead of a binary dSYM companion file."), - init(false), cat(DsymCategory), cl::Hidden); - -static opt DumpStab( - "symtab", - desc("Dumps the symbol table found in executable or object file(s) and\n" - "exits."), - init(false), cat(DsymCategory)); -static alias DumpStabA("s", desc("Alias for --symtab"), aliasopt(DumpStab)); - -static opt FlatOut("flat", - desc("Produce a flat dSYM file (not a bundle)."), - init(false), cat(DsymCategory)); -static alias FlatOutA("f", desc("Alias for --flat"), aliasopt(FlatOut)); - -static opt Minimize( - "minimize", - desc("When used when creating a dSYM file with Apple accelerator tables,\n" - "this option will suppress the emission of the .debug_inlines, \n" - ".debug_pubnames, and .debug_pubtypes sections since dsymutil \n" - "has better equivalents: .apple_names and .apple_types. When used in\n" - "conjunction with --update option, this option will cause redundant\n" - "accelerator tables to be removed."), - init(false), cat(DsymCategory)); -static alias MinimizeA("z", desc("Alias for --minimize"), aliasopt(Minimize)); - -static opt Update( - "update", - desc("Updates existing dSYM files to contain the latest accelerator\n" - "tables and other DWARF optimizations."), - init(false), cat(DsymCategory)); -static alias UpdateA("u", desc("Alias for --update"), aliasopt(Update)); - -static opt SymbolMap( - "symbol-map", - desc("Updates the existing dSYMs inplace using symbol map specified."), - value_desc("bcsymbolmap"), cat(DsymCategory)); - -static cl::opt AcceleratorTable( - "accelerator", cl::desc("Output accelerator tables."), - cl::values(clEnumValN(AccelTableKind::Default, "Default", - "Default for input."), - clEnumValN(AccelTableKind::Apple, "Apple", "Apple"), - clEnumValN(AccelTableKind::Dwarf, "Dwarf", "DWARF")), - cl::init(AccelTableKind::Default), cat(DsymCategory)); - -static opt NumThreads( - "num-threads", - desc("Specifies the maximum number (n) of simultaneous threads to use\n" - "when linking multiple architectures."), - value_desc("n"), init(0), cat(DsymCategory)); -static alias NumThreadsA("j", desc("Alias for --num-threads"), - aliasopt(NumThreads)); - -static opt Verbose("verbose", desc("Verbosity level"), init(false), - cat(DsymCategory)); - -static opt - NoOutput("no-output", - desc("Do the link in memory, but do not emit the result file."), - init(false), cat(DsymCategory)); - -static opt - NoTimestamp("no-swiftmodule-timestamp", - desc("Don't check timestamp for swiftmodule files."), - init(false), cat(DsymCategory)); - -static list ArchFlags( - "arch", - desc("Link DWARF debug information only for specified CPU architecture\n" - "types. This option can be specified multiple times, once for each\n" - "desired architecture. All CPU architectures will be linked by\n" - "default."), - value_desc("arch"), ZeroOrMore, cat(DsymCategory)); - -static opt - NoODR("no-odr", - desc("Do not use ODR (One Definition Rule) for type uniquing."), - init(false), cat(DsymCategory)); - -static opt DumpDebugMap( - "dump-debug-map", - desc("Parse and dump the debug map to standard output. Not DWARF link " - "will take place."), - init(false), cat(DsymCategory)); - -static opt InputIsYAMLDebugMap( - "y", desc("Treat the input file is a YAML debug map rather than a binary."), - init(false), cat(DsymCategory)); - -static opt Verify("verify", desc("Verify the linked DWARF debug info."), - cat(DsymCategory)); - -static opt - Toolchain("toolchain", desc("Embed toolchain information in dSYM bundle."), - cat(DsymCategory)); - -static opt - PaperTrailWarnings("papertrail", - desc("Embed warnings in the linked DWARF debug info."), - cat(DsymCategory)); - -static Error createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot) { - if (NoOutput) - return Error::success(); +namespace { +enum ID { + OPT_INVALID = 0, // This is not an option ID. +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + OPT_##ID, +#include "Options.inc" +#undef OPTION +}; + +#define PREFIX(NAME, VALUE) const char *const NAME[] = VALUE; +#include "Options.inc" +#undef PREFIX + +const opt::OptTable::Info InfoTable[] = { +#define OPTION(PREFIX, NAME, ID, KIND, GROUP, ALIAS, ALIASARGS, FLAGS, PARAM, \ + HELPTEXT, METAVAR, VALUES) \ + { \ + PREFIX, NAME, HELPTEXT, \ + METAVAR, OPT_##ID, opt::Option::KIND##Class, \ + PARAM, FLAGS, OPT_##GROUP, \ + OPT_##ALIAS, ALIASARGS, VALUES}, +#include "Options.inc" +#undef OPTION +}; + +class DsymutilOptTable : public opt::OptTable { +public: + DsymutilOptTable() : OptTable(InfoTable) {} +}; +} // namespace + +struct DsymutilOptions { + bool DumpDebugMap = false; + bool DumpStab = false; + bool Flat = false; + bool InputIsYAMLDebugMap = false; + bool PaperTrailWarnings = false; + bool Verify = false; + std::string SymbolMap; + std::string OutputFile; + std::string Toolchain; + std::vector Archs; + std::vector InputFiles; + unsigned NumThreads; + LinkOptions LinkOptions; +}; + +/// Return a list of input files. This function has logic for dealing with the +/// special case where we might have dSYM bundles as input. The function +/// returns an error when the directory structure doesn't match that of a dSYM +/// bundle. +static Expected> getInputs(opt::InputArgList &Args, + bool DsymAsInput) { + std::vector InputFiles; + for (auto *File : Args.filtered(OPT_INPUT)) + InputFiles.push_back(File->getValue()); + + if (!DsymAsInput) + return InputFiles; + + // If we are updating, we might get dSYM bundles as input. + std::vector Inputs; + for (const auto &Input : InputFiles) { + if (!llvm::sys::fs::is_directory(Input)) { + Inputs.push_back(Input); + continue; + } + + // Make sure that we're dealing with a dSYM bundle. + SmallString<256> BundlePath(Input); + sys::path::append(BundlePath, "Contents", "Resources", "DWARF"); + if (!llvm::sys::fs::is_directory(BundlePath)) + return make_error( + Input + " is a directory, but doesn't look like a dSYM bundle.", + inconvertibleErrorCode()); + + // Create a directory iterator to iterate over all the entries in the + // bundle. + std::error_code EC; + llvm::sys::fs::directory_iterator DirIt(BundlePath, EC); + llvm::sys::fs::directory_iterator DirEnd; + if (EC) + return errorCodeToError(EC); + + // Add each entry to the list of inputs. + while (DirIt != DirEnd) { + Inputs.push_back(DirIt->path()); + DirIt.increment(EC); + if (EC) + return errorCodeToError(EC); + } + } + return Inputs; +} + +// Verify that the given combination of options makes sense. +static llvm::Error verifyOptions(const DsymutilOptions &Options) { + if (Options.LinkOptions.Update && + std::find(Options.InputFiles.begin(), Options.InputFiles.end(), "-") != + Options.InputFiles.end()) { + // FIXME: We cannot use stdin for an update because stdin will be + // consumed by the BinaryHolder during the debugmap parsing, and + // then we will want to consume it again in DwarfLinker. If we + // used a unique BinaryHolder object that could cache multiple + // binaries this restriction would go away. + return make_error( + "standard input cannot be used as input for a dSYM update.", + errc::invalid_argument); + } + + if (!Options.Flat && Options.OutputFile == "-") + return make_error( + "cannot emit to standard output without --flat.", + errc::invalid_argument); + + if (Options.InputFiles.size() > 1 && Options.Flat && + !Options.OutputFile.empty()) + return make_error( + "cannot use -o with multiple inputs in flat mode.", + errc::invalid_argument); + + if (Options.PaperTrailWarnings && Options.InputIsYAMLDebugMap) + return make_error( + "paper trail warnings are not supported for YAML input.", + errc::invalid_argument); + + return Error::success(); +} + +static Expected getAccelTableKind(opt::InputArgList &Args) { + if (opt::Arg *Accelerator = Args.getLastArg(OPT_accelerator)) { + StringRef S = Accelerator->getValue(); + if (S == "Apple") + return AccelTableKind::Apple; + if (S == "Dwarf") + return AccelTableKind::Dwarf; + if (S == "Default") + return AccelTableKind::Default; + return make_error( + "invalid accelerator type specified: '" + S + + "'. Support values are 'Apple', 'Dwarf' and 'Default'.", + inconvertibleErrorCode()); + } + return AccelTableKind::Default; +} + +/// Parses the command line options into the LinkOptions struct and performs +/// some sanity checking. Returns an error in case the latter fails. +static Expected getOptions(opt::InputArgList &Args) { + DsymutilOptions Options; + + Options.DumpDebugMap = Args.hasArg(OPT_dump_debug_map); + Options.DumpStab = Args.hasArg(OPT_symtab); + Options.Flat = Args.hasArg(OPT_flat); + Options.InputIsYAMLDebugMap = Args.hasArg(OPT_yaml_input); + Options.PaperTrailWarnings = Args.hasArg(OPT_papertrail); + Options.Verify = Args.hasArg(OPT_verify); + + Options.LinkOptions.Minimize = Args.hasArg(OPT_minimize); + Options.LinkOptions.NoODR = Args.hasArg(OPT_no_odr); + Options.LinkOptions.NoOutput = Args.hasArg(OPT_no_output); + Options.LinkOptions.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp); + Options.LinkOptions.Update = Args.hasArg(OPT_update); + Options.LinkOptions.Verbose = Args.hasArg(OPT_verbose); + + if (Expected AccelKind = getAccelTableKind(Args)) { + Options.LinkOptions.TheAccelTableKind = *AccelKind; + } else { + return AccelKind.takeError(); + } + + if (opt::Arg *SymbolMap = Args.getLastArg(OPT_symbolmap)) + Options.SymbolMap = SymbolMap->getValue(); + + if (Args.hasArg(OPT_symbolmap)) + Options.LinkOptions.Update = true; + + if (Expected> InputFiles = + getInputs(Args, Options.LinkOptions.Update)) { + Options.InputFiles = std::move(*InputFiles); + } else { + return InputFiles.takeError(); + } + + for (auto *Arch : Args.filtered(OPT_arch)) + Options.Archs.push_back(Arch->getValue()); + + if (opt::Arg *OsoPrependPath = Args.getLastArg(OPT_oso_prepend_path)) + Options.LinkOptions.PrependPath = OsoPrependPath->getValue(); + + if (opt::Arg *OutputFile = Args.getLastArg(OPT_output)) + Options.OutputFile = OutputFile->getValue(); + + if (opt::Arg *Toolchain = Args.getLastArg(OPT_toolchain)) + Options.Toolchain = Toolchain->getValue(); + + if (Args.hasArg(OPT_assembly)) + Options.LinkOptions.FileType = OutputFileType::Assembly; + + if (opt::Arg *NumThreads = Args.getLastArg(OPT_threads)) + Options.LinkOptions.Threads = atoi(NumThreads->getValue()); + else + Options.LinkOptions.Threads = llvm::thread::hardware_concurrency(); + + if (Options.DumpDebugMap || Options.LinkOptions.Verbose) + Options.LinkOptions.Threads = 1; + if (getenv("RC_DEBUG_OPTIONS")) + Options.PaperTrailWarnings = true; + + if (Error E = verifyOptions(Options)) + return std::move(E); + return Options; +} + +static Error createPlistFile(llvm::StringRef Bin, llvm::StringRef BundleRoot, + llvm::StringRef Toolchain) { // Create plist file to write to. llvm::SmallString<128> InfoPlist(BundleRoot); llvm::sys::path::append(InfoPlist, "Contents/Info.plist"); @@ -237,9 +331,6 @@ } static Error createBundleDir(llvm::StringRef BundleBase) { - if (NoOutput) - return Error::success(); - llvm::SmallString<128> Bundle(BundleBase); llvm::sys::path::append(Bundle, "Contents", "Resources", "DWARF"); if (std::error_code EC = @@ -250,7 +341,8 @@ return Error::success(); } -static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch) { +static bool verify(llvm::StringRef OutputFile, llvm::StringRef Arch, + bool Verbose) { if (OutputFile == "-") { WithColor::warning() << "verification skipped for " << Arch << "because writing to stdout.\n"; @@ -288,25 +380,27 @@ std::string DWARFFile; llvm::Optional ResourceDir; }; -} +} // namespace -static Expected getOutputFileName(llvm::StringRef InputFile) { - if (OutputFileOpt == "-") - return OutputLocation(OutputFileOpt); +static Expected +getOutputFileName(llvm::StringRef InputFile, const DsymutilOptions &Options) { + if (Options.OutputFile == "-") + return OutputLocation(Options.OutputFile); // When updating, do in place replacement. - if (OutputFileOpt.empty() && (Update || !SymbolMap.empty())) + if (Options.OutputFile.empty() && + (Options.LinkOptions.Update || !Options.SymbolMap.empty())) return OutputLocation(InputFile); // If a flat dSYM has been requested, things are pretty simple. - if (FlatOut) { - if (OutputFileOpt.empty()) { + if (Options.Flat) { + if (Options.OutputFile.empty()) { if (InputFile == "-") return OutputLocation{"a.out.dwarf", {}}; return OutputLocation((InputFile + ".dwarf").str()); } - return OutputLocation(OutputFileOpt); + return OutputLocation(Options.OutputFile); } // We need to create/update a dSYM bundle. @@ -319,13 +413,15 @@ // std::string DwarfFile = InputFile == "-" ? llvm::StringRef("a.out") : InputFile; - llvm::SmallString<128> Path(OutputFileOpt); + llvm::SmallString<128> Path(Options.OutputFile); if (Path.empty()) Path = DwarfFile + ".dSYM"; - if (auto E = createBundleDir(Path)) - return std::move(E); - if (auto E = createPlistFile(DwarfFile, Path)) - return std::move(E); + if (!Options.LinkOptions.NoOutput) { + if (auto E = createBundleDir(Path)) + return std::move(E); + if (auto E = createPlistFile(DwarfFile, Path, Options.Toolchain)) + return std::move(E); + } llvm::sys::path::append(Path, "Contents", "Resources"); std::string ResourceDir = Path.str(); @@ -333,177 +429,71 @@ return OutputLocation(Path.str(), ResourceDir); } -/// Parses the command line options into the LinkOptions struct and performs -/// some sanity checking. Returns an error in case the latter fails. -static Expected getOptions() { - LinkOptions Options; - - Options.Verbose = Verbose; - Options.NoOutput = NoOutput; - Options.NoODR = NoODR; - Options.Minimize = Minimize; - Options.Update = Update; - Options.NoTimestamp = NoTimestamp; - Options.PrependPath = OsoPrependPath; - Options.TheAccelTableKind = AcceleratorTable; - - if (!SymbolMap.empty()) - Options.Update = true; - - if (Assembly) - Options.FileType = OutputFileType::Assembly; - - if (Options.Update && std::find(InputFiles.begin(), InputFiles.end(), "-") != - InputFiles.end()) { - // FIXME: We cannot use stdin for an update because stdin will be - // consumed by the BinaryHolder during the debugmap parsing, and - // then we will want to consume it again in DwarfLinker. If we - // used a unique BinaryHolder object that could cache multiple - // binaries this restriction would go away. - return make_error( - "standard input cannot be used as input for a dSYM update.", - inconvertibleErrorCode()); - } - - if (NumThreads == 0) - Options.Threads = llvm::thread::hardware_concurrency(); - else - Options.Threads = NumThreads; - if (DumpDebugMap || Verbose) - Options.Threads = 1; - - return Options; -} - -/// Return a list of input files. This function has logic for dealing with the -/// special case where we might have dSYM bundles as input. The function -/// returns an error when the directory structure doesn't match that of a dSYM -/// bundle. -static Expected> getInputs(bool DsymAsInput) { - if (!DsymAsInput) - return InputFiles; - - // If we are updating, we might get dSYM bundles as input. - std::vector Inputs; - for (const auto &Input : InputFiles) { - if (!llvm::sys::fs::is_directory(Input)) { - Inputs.push_back(Input); - continue; - } - - // Make sure that we're dealing with a dSYM bundle. - SmallString<256> BundlePath(Input); - sys::path::append(BundlePath, "Contents", "Resources", "DWARF"); - if (!llvm::sys::fs::is_directory(BundlePath)) - return make_error( - Input + " is a directory, but doesn't look like a dSYM bundle.", - inconvertibleErrorCode()); - - // Create a directory iterator to iterate over all the entries in the - // bundle. - std::error_code EC; - llvm::sys::fs::directory_iterator DirIt(BundlePath, EC); - llvm::sys::fs::directory_iterator DirEnd; - if (EC) - return errorCodeToError(EC); - - // Add each entry to the list of inputs. - while (DirIt != DirEnd) { - Inputs.push_back(DirIt->path()); - DirIt.increment(EC); - if (EC) - return errorCodeToError(EC); - } - } - return Inputs; -} - int main(int argc, char **argv) { InitLLVM X(argc, argv); + // Parse arguments. + DsymutilOptTable T; + unsigned MAI; + unsigned MAC; + ArrayRef ArgsArr = makeArrayRef(argv + 1, argc - 1); + opt::InputArgList Args = T.ParseArgs(ArgsArr, MAI, MAC); + void *P = (void *)(intptr_t)getOutputFileName; std::string SDKPath = llvm::sys::fs::getMainExecutable(argv[0], P); SDKPath = llvm::sys::path::parent_path(SDKPath); - HideUnrelatedOptions({&DsymCategory, &ColorCategory}); - llvm::cl::ParseCommandLineOptions( - argc, argv, - "manipulate archived DWARF debug symbol files.\n\n" - "dsymutil links the DWARF debug information found in the object files\n" - "for the executable by using debug symbols information\n" - "contained in its symbol table.\n"); - - if (Help) { - PrintHelpMessage(); + if (Args.hasArg(OPT_help)) { + T.PrintHelp( + llvm::outs(), + (std::string(argv[0]) + " [options] ").c_str(), + "manipulate archived DWARF debug symbol files.\n\n" + "dsymutil links the DWARF debug information found in the object files\n" + "for the executable by using debug symbols information\n" + "contained in its symbol table.\n", + false); return 0; } - if (Version) { + if (Args.hasArg(OPT_version)) { llvm::cl::PrintVersionMessage(); return 0; } - auto OptionsOrErr = getOptions(); + auto OptionsOrErr = getOptions(Args); if (!OptionsOrErr) { WithColor::error() << toString(OptionsOrErr.takeError()); return 1; } + auto &Options = *OptionsOrErr; + llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllTargets(); llvm::InitializeAllAsmPrinters(); - auto InputsOrErr = getInputs(OptionsOrErr->Update); - if (!InputsOrErr) { - WithColor::error() << toString(InputsOrErr.takeError()) << '\n'; - return 1; - } - - if (!FlatOut && OutputFileOpt == "-") { - WithColor::error() << "cannot emit to standard output without --flat\n"; - return 1; - } - - if (InputsOrErr->size() > 1 && FlatOut && !OutputFileOpt.empty()) { - WithColor::error() << "cannot use -o with multiple inputs in flat mode\n"; - return 1; - } - - if (InputFiles.size() > 1 && !SymbolMap.empty() && - !llvm::sys::fs::is_directory(SymbolMap)) { - WithColor::error() << "when unobfuscating multiple files, --symbol-map " - << "needs to point to a directory.\n"; - return 1; - } - - if (getenv("RC_DEBUG_OPTIONS")) - PaperTrailWarnings = true; - - if (PaperTrailWarnings && InputIsYAMLDebugMap) - WithColor::warning() - << "Paper trail warnings are not supported for YAML input"; - - for (const auto &Arch : ArchFlags) + for (const auto &Arch : Options.Archs) if (Arch != "*" && Arch != "all" && !llvm::object::MachOObjectFile::isValidArch(Arch)) { WithColor::error() << "unsupported cpu architecture: '" << Arch << "'\n"; return 1; } - SymbolMapLoader SymMapLoader(SymbolMap); + SymbolMapLoader SymMapLoader(Options.SymbolMap); - for (auto &InputFile : *InputsOrErr) { + for (auto &InputFile : Options.InputFiles) { // Dump the symbol table for each input file and requested arch - if (DumpStab) { - if (!dumpStab(InputFile, ArchFlags, OsoPrependPath)) + if (Options.DumpStab) { + if (!dumpStab(InputFile, Options.Archs, Options.LinkOptions.PrependPath)) return 1; continue; } auto DebugMapPtrsOrErr = - parseDebugMap(InputFile, ArchFlags, OsoPrependPath, PaperTrailWarnings, - Verbose, InputIsYAMLDebugMap); + parseDebugMap(InputFile, Options.Archs, Options.LinkOptions.PrependPath, + Options.PaperTrailWarnings, Options.LinkOptions.Verbose, + Options.InputIsYAMLDebugMap); if (auto EC = DebugMapPtrsOrErr.getError()) { WithColor::error() << "cannot parse the debug map for '" << InputFile @@ -511,7 +501,7 @@ return 1; } - if (OptionsOrErr->Update) { + if (Options.LinkOptions.Update) { // The debug map should be empty. Add one object file corresponding to // the input file. for (auto &Map : *DebugMapPtrsOrErr) @@ -528,27 +518,27 @@ // Shared a single binary holder for all the link steps. BinaryHolder BinHolder; - unsigned ThreadCount = - std::min(OptionsOrErr->Threads, DebugMapPtrsOrErr->size()); + unsigned ThreadCount = std::min(Options.LinkOptions.Threads, + DebugMapPtrsOrErr->size()); llvm::ThreadPool Threads(ThreadCount); // If there is more than one link to execute, we need to generate // temporary files. bool NeedsTempFiles = - !DumpDebugMap && (OutputFileOpt != "-") && - (DebugMapPtrsOrErr->size() != 1 || OptionsOrErr->Update); + !Options.DumpDebugMap && (Options.OutputFile != "-") && + (DebugMapPtrsOrErr->size() != 1 || Options.LinkOptions.Update); llvm::SmallVector TempFiles; std::atomic_char AllOK(1); for (auto &Map : *DebugMapPtrsOrErr) { - if (Verbose || DumpDebugMap) + if (Options.LinkOptions.Verbose || Options.DumpDebugMap) Map->print(llvm::outs()); - if (DumpDebugMap) + if (Options.DumpDebugMap) continue; - if (!SymbolMap.empty()) - OptionsOrErr->Translator = SymMapLoader.Load(InputFile, *Map); + if (!Options.SymbolMap.empty()) + Options.LinkOptions.Translator = SymMapLoader.Load(InputFile, *Map); if (Map->begin() == Map->end()) WithColor::warning() @@ -560,12 +550,12 @@ std::shared_ptr OS; Expected OutputLocationOrErr = - getOutputFileName(InputFile); + getOutputFileName(InputFile, Options); if (!OutputLocationOrErr) { WithColor::error() << toString(OutputLocationOrErr.takeError()); return 1; } - OptionsOrErr->ResourceDir = OutputLocationOrErr->getResourceDir(); + Options.LinkOptions.ResourceDir = OutputLocationOrErr->getResourceDir(); std::string OutputFile = OutputLocationOrErr->DWARFFile; if (NeedsTempFiles) { @@ -583,30 +573,33 @@ OutputFile = TempFile.TmpName; } else { std::error_code EC; - OS = std::make_shared(NoOutput ? "-" : OutputFile, EC, - sys::fs::OF_None); + OS = std::make_shared( + Options.LinkOptions.NoOutput ? "-" : OutputFile, EC, + sys::fs::OF_None); if (EC) { WithColor::error() << OutputFile << ": " << EC.message(); return 1; } } + const bool Verify = Options.Verify && !Options.LinkOptions.NoOutput; auto LinkLambda = [&, OutputFile](std::shared_ptr Stream, LinkOptions Options) { AllOK.fetch_and( linkDwarf(*Stream, BinHolder, *Map, std::move(Options))); Stream->flush(); - if (Verify && !NoOutput) - AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName())); + if (Verify) + AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName(), + Options.Verbose)); }; // FIXME: The DwarfLinker can have some very deep recursion that can max // out the (significantly smaller) stack when using threads. We don't // want this limitation when we only have a single thread. if (ThreadCount == 1) - LinkLambda(OS, *OptionsOrErr); + LinkLambda(OS, Options.LinkOptions); else - Threads.async(LinkLambda, OS, *OptionsOrErr); + Threads.async(LinkLambda, OS, Options.LinkOptions); } Threads.wait(); @@ -615,14 +608,15 @@ return 1; if (NeedsTempFiles) { - Expected OutputLocationOrErr = getOutputFileName(InputFile); + Expected OutputLocationOrErr = + getOutputFileName(InputFile, Options); if (!OutputLocationOrErr) { WithColor::error() << toString(OutputLocationOrErr.takeError()); return 1; } if (!MachOUtils::generateUniversalBinary(TempFiles, OutputLocationOrErr->DWARFFile, - *OptionsOrErr, SDKPath)) + Options.LinkOptions, SDKPath)) return 1; } }