Index: llvm/include/llvm/DWARFLinker/DWARFLinker.h =================================================================== --- llvm/include/llvm/DWARFLinker/DWARFLinker.h +++ llvm/include/llvm/DWARFLinker/DWARFLinker.h @@ -272,6 +272,9 @@ /// Print statistics to standard output. void setStatistics(bool Statistics) { Options.Statistics = Statistics; } + /// Verify the input DWARF. + void setVerify(bool Verify) { Options.Verify = Verify; } + /// Do not emit linked dwarf info. void setNoOutput(bool NoOut) { Options.NoOutput = NoOut; } @@ -389,6 +392,9 @@ AncestorIdx(AncestorIdx) {} }; + /// Verify the given DWARF file. + bool verify(const DWARFFile &File); + /// returns true if we need to translate strings. bool needToTranslateStrings() { return StringsTranslator != nullptr; } @@ -778,6 +784,9 @@ /// Print statistics. bool Statistics = false; + /// Verify DWARF + bool Verify = false; + /// Skip emitting output bool NoOutput = false; Index: llvm/lib/DWARFLinker/DWARFLinker.cpp =================================================================== --- llvm/lib/DWARFLinker/DWARFLinker.cpp +++ llvm/lib/DWARFLinker/DWARFLinker.cpp @@ -2362,6 +2362,10 @@ if (!OptContext.File.Dwarf) continue; + + if (Options.Verify) + verify(OptContext.File); + // Look for relocations that correspond to address map entries. // there was findvalidrelocations previously ... probably we need to gather @@ -2631,4 +2635,16 @@ return true; } +bool DWARFLinker::verify(const DWARFFile &File) { + assert(File.Dwarf); + + DIDumpOptions DumpOpts; + if (!File.Dwarf->verify(llvm::outs(), DumpOpts.noImplicitRecursion())) { + WithColor::warning() << "input verification failed for " << File.FileName + << '\n'; + return false; + } + return true; +} + } // namespace llvm Index: llvm/test/tools/dsymutil/X86/verify.test =================================================================== --- llvm/test/tools/dsymutil/X86/verify.test +++ llvm/test/tools/dsymutil/X86/verify.test @@ -3,14 +3,22 @@ # RUN: dsymutil -verify -verbose -oso-prepend-path=%p/.. %p/../Inputs/basic.macho.x86_64 %p/../Inputs/basic-archive.macho.x86_64 %p/../Inputs/basic-lto.macho.x86_64 %p/../Inputs/basic-lto-dw4.macho.x86_64 -o %t 2>&1 | FileCheck %s --check-prefixes=QUIET-SUCCESS,VERBOSE # VERBOSE: Verifying DWARF for architecture: x86_64 -# QUIET-SUCCESS-NOT: error: verification failed +# QUIET-SUCCESS-NOT: error: output verification failed -# Negative tests in regular and verbose mode. +# Negative output tests in regular and verbose mode. # (Invalid object generated from ../Inputs/invalid.s by modified the low PC.) -# RUN: not dsymutil -verify -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefix=QUIET-FAIL -# RUN: not dsymutil -verify -verbose -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefixes=QUIET-FAIL,VERBOSE +# RUN: not dsymutil -verify -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefix=QUIET-OUTPUT-FAIL +# RUN: not dsymutil -verify-dwarf=output -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefix=QUIET-OUTPUT-FAIL +# RUN: not dsymutil -verify -verbose -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefixes=QUIET-OUTPUT-FAIL,VERBOSE -# QUIET-FAIL: error: verification failed +# Negative input & output tests in regular and verbose mode. Only output failures result in a non-zero exit code. +# RUN: dsymutil -verify-dwarf=input -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefix=QUIET-INPUT-FAIL +# RUN: dsymutil -verify-dwarf=input -verbose -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefix=QUIET-INPUT-FAIL +# RUN: not dsymutil -verify-dwarf=all -oso-prepend-path=%p/../Inputs -y %s -o %t 2>&1 | FileCheck %s --check-prefixes=QUIET-OUTPUT-FAIL,QUIET-INPUT-FAIL,VERBOSE-INPUT-FAIL + +# VERBOSE-INPUT-FAIL: error: Abbreviation declaration contains multiple DW_AT_language attributes. +# QUIET-INPUT-FAIL: warning: input verification failed for {{.*}}invalid.o +# QUIET-OUTPUT-FAIL: error: output verification failed --- triple: 'x86_64-apple-darwin' Index: llvm/tools/dsymutil/DwarfLinkerForBinary.cpp =================================================================== --- llvm/tools/dsymutil/DwarfLinkerForBinary.cpp +++ llvm/tools/dsymutil/DwarfLinkerForBinary.cpp @@ -391,6 +391,7 @@ GeneralLinker.setVerbosity(Options.Verbose); GeneralLinker.setStatistics(Options.Statistics); + GeneralLinker.setVerify(Options.Verify); GeneralLinker.setNoOutput(Options.NoOutput); GeneralLinker.setNoODR(Options.NoODR); GeneralLinker.setUpdate(Options.Update); Index: llvm/tools/dsymutil/LinkUtils.h =================================================================== --- llvm/tools/dsymutil/LinkUtils.h +++ llvm/tools/dsymutil/LinkUtils.h @@ -30,6 +30,9 @@ /// Statistics bool Statistics = false; + /// Verify DWARF + bool Verify = false; + /// Skip emitting output bool NoOutput = false; Index: llvm/tools/dsymutil/Options.td =================================================================== --- llvm/tools/dsymutil/Options.td +++ llvm/tools/dsymutil/Options.td @@ -38,9 +38,15 @@ Group; def verify: F<"verify">, - HelpText<"Run the DWARF verifier on the linked DWARF debug info.">, + HelpText<"Alias for --verify-dwarf=output">, Group; +def verify_dwarf: Separate<["--", "-"], "verify-dwarf">, + MetaVarName<"">, + HelpText<"Run the DWARF verifier on the input and/or output. Valid options are 'input', 'output', 'all' or 'none'.">, + Group; +def: Joined<["--", "-"], "verify-dwarf=">, Alias; + def no_output: F<"no-output">, HelpText<"Do the link in memory, but do not emit the result file.">, Group; Index: llvm/tools/dsymutil/dsymutil.cpp =================================================================== --- llvm/tools/dsymutil/dsymutil.cpp +++ llvm/tools/dsymutil/dsymutil.cpp @@ -85,13 +85,28 @@ }; } // namespace +enum class DWARFVerify : unsigned { + None = 0, + Input = 1 << 0, + Output = 1 << 1, +}; + +inline DWARFVerify operator|(DWARFVerify LHS, DWARFVerify RHS) { + return static_cast(static_cast(LHS) | + static_cast(RHS)); +} + +inline DWARFVerify operator&(DWARFVerify LHS, DWARFVerify RHS) { + return static_cast(static_cast(LHS) & + static_cast(RHS)); +} + struct DsymutilOptions { bool DumpDebugMap = false; bool DumpStab = false; bool Flat = false; bool InputIsYAMLDebugMap = false; bool PaperTrailWarnings = false; - bool Verify = false; bool ForceKeepFunctionForStatic = false; std::string SymbolMap; std::string OutputFile; @@ -100,6 +115,7 @@ std::vector Archs; std::vector InputFiles; unsigned NumThreads; + DWARFVerify Verify = DWARFVerify::None; ReproducerMode ReproMode = ReproducerMode::Off; dsymutil::LinkOptions LinkOpts; }; @@ -214,6 +230,27 @@ return AccelTableKind::Default; } +static Expected getVerifyKind(opt::InputArgList &Args) { + if (Args.hasArg(OPT_verify)) + return DWARFVerify::Output; + if (opt::Arg *Verify = Args.getLastArg(OPT_verify_dwarf)) { + StringRef S = Verify->getValue(); + if (S == "input") + return DWARFVerify::Input; + if (S == "output") + return DWARFVerify::Output; + if (S == "all") + return DWARFVerify::Input | DWARFVerify::Output; + if (S == "none") + return DWARFVerify::None; + return make_error( + "invalid verify type specified: '" + S + + "'. Support values are 'input', 'output', 'all' and 'none'.", + inconvertibleErrorCode()); + } + return DWARFVerify::None; +} + /// 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) { @@ -224,9 +261,16 @@ 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); + + if (Expected Verify = getVerifyKind(Args)) { + Options.Verify = *Verify; + } else { + return Verify.takeError(); + } Options.LinkOpts.NoODR = Args.hasArg(OPT_no_odr); + Options.LinkOpts.Verify = + static_cast(Options.Verify & DWARFVerify::Input); Options.LinkOpts.NoOutput = Args.hasArg(OPT_no_output); Options.LinkOpts.NoTimestamp = Args.hasArg(OPT_no_swiftmodule_timestamp); Options.LinkOpts.Update = Args.hasArg(OPT_update); @@ -388,7 +432,7 @@ return Error::success(); } -static bool verify(StringRef OutputFile, StringRef Arch, bool Verbose) { +static bool verifyOutput(StringRef OutputFile, StringRef Arch, bool Verbose) { if (OutputFile == "-") { WithColor::warning() << "verification skipped for " << Arch << "because writing to stdout.\n"; @@ -409,7 +453,7 @@ DIDumpOptions DumpOpts; bool success = DICtx->verify(os, DumpOpts.noImplicitRecursion()); if (!success) - WithColor::error() << "verification failed for " << Arch << '\n'; + WithColor::error() << "output verification failed for " << Arch << '\n'; return success; } @@ -613,7 +657,12 @@ const bool NeedsTempFiles = !Options.DumpDebugMap && (Options.OutputFile != "-") && (DebugMapPtrsOrErr->size() != 1 || Options.LinkOpts.Update); - const bool Verify = Options.Verify && !Options.LinkOpts.NoOutput; + bool VerifyOutput = static_cast(Options.Verify & DWARFVerify::Output); + if (VerifyOutput && Options.LinkOpts.NoOutput) { + WithColor::warning() + << "skipping output verification because --no-output was passed\n"; + VerifyOutput = false; + } SmallVector TempFiles; std::atomic_char AllOK(1); @@ -665,9 +714,10 @@ AllOK.fetch_and( linkDwarf(*Stream, BinHolder, *Map, std::move(Options))); Stream->flush(); - if (Verify) - AllOK.fetch_and(verify(OutputFile, Map->getTriple().getArchName(), - Options.Verbose)); + if (VerifyOutput) { + AllOK.fetch_and(verifyOutput( + OutputFile, Map->getTriple().getArchName(), Options.Verbose)); + } }; // FIXME: The DwarfLinker can have some very deep recursion that can max