diff --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst --- a/llvm/docs/CommandGuide/llvm-objdump.rst +++ b/llvm/docs/CommandGuide/llvm-objdump.rst @@ -129,6 +129,19 @@ Demangle symbol names in the output. +.. option:: --debug-file-directory + + Provide a path to a directory with a `.build-id` subdirectory to search for + debug information for stripped binaries. Multiple instances of this argument + are searched in the order given. + +.. option:: --debuginfod, --no-debuginfod + + Whether or not to try debuginfod lookups for debug binaries. Unless specified, + debuginfod is only enabled if libcurl was compiled in (``LLVM_ENABLE_CURL``) + and at least one server URL was provided by the environment variable + ``DEBUGINFOD_URLS``. + .. option:: --debug-vars= Print the locations (in registers or memory) of source-level variables diff --git a/llvm/docs/CommandGuide/llvm-symbolizer.rst b/llvm/docs/CommandGuide/llvm-symbolizer.rst --- a/llvm/docs/CommandGuide/llvm-symbolizer.rst +++ b/llvm/docs/CommandGuide/llvm-symbolizer.rst @@ -221,6 +221,12 @@ ``auto``, which detects whether standard output supports color. Specifying ``--color`` alone is equivalent to ``--color=always``. +.. option:: --debug-file-directory + + Provide a path to a directory with a `.build-id` subdirectory to search for + debug information for stripped binaries. Multiple instances of this argument + are searched in the order given. + .. option:: --debuginfod, --no-debuginfod Whether or not to try debuginfod lookups for debug binaries. Unless specified, diff --git a/llvm/test/tools/llvm-objdump/Inputs/embedded-source b/llvm/test/tools/llvm-objdump/Inputs/embedded-source old mode 100644 new mode 100755 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ %t/broken/llvmcache-7361776989772977641 + +# Write the stripped binary under %t/stripped-cache. +RUN: mkdir %t/stripped-cache +RUN: cp %t/stripped %t/stripped-cache/llvmcache-7361776989772977641 + +# Write to a debug info directory as well. +RUN: mkdir -p %t/debug/.build-id/15 +RUN: cp %p/Inputs/embedded-source %t/debug/.build-id/15/12f769114c011387393822af15dd660c080295.debug + +# Don't use debuginfod by default without any URLs. +RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-objdump -d --source %t/stripped | \ +RUN: FileCheck %s --check-prefix=NOTFOUND + +# Don't use debuginfod if disabled. +RUN: env DEBUGINFOD_CACHE_PATH=%t DEBUGINFOD_URLS=http://foo \ +RUN: llvm-objdump -d --source --no-debuginfod %t/stripped | \ +RUN: FileCheck %s --check-prefix=NOTFOUND + +# Look up build IDs locally without debuginfod. +RUN: llvm-objdump -d --source --no-debuginfod --debug-file-directory %t/debug \ +RUN: %t/stripped | \ +RUN: FileCheck %s --check-prefix=FOUND + +# Use debuginfod without URLs if requested. +RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-objdump -d --source --debuginfod \ +RUN: %t/stripped | \ +RUN: FileCheck %s --check-prefix=FOUND + +# Produce a warning if a bad binary is fetched, but do not fail. +RUN: env DEBUGINFOD_CACHE_PATH=%t/broken llvm-objdump -d --source --debuginfod \ +RUN: %t/stripped 2> %t.err | \ +RUN: FileCheck %s --check-prefix=NOTFOUND +RUN: FileCheck %s --check-prefix=BADBINARYERROR -DPATH=%t --input-file %t.err +BADBINARYERROR: warning: '[[PATH]]/broken{{[/\\]}}llvmcache-7361776989772977641': The file was not recognized as a valid object file + +# Use the original binary if the fetched binary has no debug info. +RUN: env DEBUGINFOD_CACHE_PATH=%t/stripped-cache llvm-objdump -d --source \ +RUN: --debuginfod %t/stripped 2> %t.err | \ +RUN: FileCheck %s --check-prefix=NOTFOUND +RUN: count 0 < %t.err + +NOTFOUND-NOT: int main(int argc, char *argv[]) { +FOUND: int main(int argc, char *argv[]) { diff --git a/llvm/tools/llvm-objdump/CMakeLists.txt b/llvm/tools/llvm-objdump/CMakeLists.txt --- a/llvm/tools/llvm-objdump/CMakeLists.txt +++ b/llvm/tools/llvm-objdump/CMakeLists.txt @@ -36,6 +36,8 @@ OtoolOptsTableGen ) +target_link_libraries(llvm-objdump PRIVATE LLVMDebuginfod) + if(LLVM_HAVE_LIBXAR) target_link_libraries(llvm-objdump PRIVATE ${XAR_LIB}) endif() diff --git a/llvm/tools/llvm-objdump/ObjdumpOpts.td b/llvm/tools/llvm-objdump/ObjdumpOpts.td --- a/llvm/tools/llvm-objdump/ObjdumpOpts.td +++ b/llvm/tools/llvm-objdump/ObjdumpOpts.td @@ -1,5 +1,10 @@ include "llvm/Option/OptParser.td" +multiclass B { + def NAME: Flag<["--"], name>, HelpText; + def no_ # NAME: Flag<["--"], "no-" # name>, HelpText; +} + multiclass Eq { def NAME : Separate<["--"], name>; def NAME #_eq : Joined<["--"], name #"=">, @@ -39,6 +44,12 @@ def demangle : Flag<["--"], "demangle">, HelpText<"Demangle symbol names">; def : Flag<["-"], "C">, Alias, HelpText<"Alias for --demangle">; +defm debug_file_directory : + Eq<"debug-file-directory", "Path to directory where to look for debug files">, + MetaVarName<"">; + +defm debuginfod : B<"debuginfod", "Use debuginfod to find debug files", "Don't use debuginfod to find debug files">; + def disassemble : Flag<["--"], "disassemble">, HelpText<"Disassemble all executable sections found in the input files">; def : Flag<["-"], "d">, Alias, HelpText<"Alias for --disassemble">; diff --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp --- a/llvm/tools/llvm-objdump/llvm-objdump.cpp +++ b/llvm/tools/llvm-objdump/llvm-objdump.cpp @@ -36,6 +36,9 @@ #include "llvm/DebugInfo/DWARF/DWARFContext.h" #include "llvm/DebugInfo/Symbolize/SymbolizableModule.h" #include "llvm/DebugInfo/Symbolize/Symbolize.h" +#include "llvm/Debuginfod/BuildIDFetcher.h" +#include "llvm/Debuginfod/Debuginfod.h" +#include "llvm/Debuginfod/HTTPClient.h" #include "llvm/Demangle/Demangle.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCContext.h" @@ -51,6 +54,7 @@ #include "llvm/MC/MCTargetOptions.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Object/Archive.h" +#include "llvm/Object/BuildID.h" #include "llvm/Object/COFF.h" #include "llvm/Object/COFFImportFile.h" #include "llvm/Object/ELFObjectFile.h" @@ -233,6 +237,9 @@ StringSet<> objdump::FoundSectionSet; static StringRef ToolName; +std::unique_ptr BIDFetcher; +ExitOnError ExitOnErr; + namespace { struct FilterResult { // True if the section should not be skipped. @@ -1258,6 +1265,24 @@ llvm_unreachable("Unsupported binary format"); } +// Tries to fetch a more complete version of the given object file using its +// Build ID. Returns None if nothing was found. +static Optional> +fetchBinaryByBuildID(const ObjectFile &Obj) { + Optional BuildID = getBuildID(&Obj); + if (!BuildID) + return None; + Optional Path = BIDFetcher->fetch(*BuildID); + if (!Path) + return None; + Expected> DebugBinary = createBinary(*Path); + if (!DebugBinary) { + reportWarning(toString(DebugBinary.takeError()), *Path); + return None; + } + return std::move(*DebugBinary); +} + static void disassembleObject(const Target *TheTarget, ObjectFile &Obj, MCContext &Ctx, MCDisassembler *PrimaryDisAsm, MCDisassembler *SecondaryDisAsm, @@ -2043,7 +2068,21 @@ IP->setMCInstrAnalysis(MIA.get()); PrettyPrinter &PIP = selectPrettyPrinter(Triple(TripleName)); - SourcePrinter SP(Obj, TheTarget->getName()); + ObjectFile *DbgObj = Obj; + OwningBinary DebugBinary; + if (!Obj->hasDebugInfo()) { + if (Optional> DebugBinaryOpt = + fetchBinaryByBuildID(*Obj)) { + if (ObjectFile *FetchedObj = + dyn_cast(DebugBinaryOpt->getBinary())) { + if (FetchedObj->hasDebugInfo()) { + DebugBinary = std::move(*DebugBinaryOpt); + DbgObj = FetchedObj; + } + } + } + } + SourcePrinter SP(DbgObj, TheTarget->getName()); for (StringRef Opt : DisassemblerOptions) if (!IP->applyTargetSpecificCLOption(Opt)) @@ -3080,6 +3119,22 @@ return 0; } + // Initialize debuginfod. + const bool ShouldUseDebuginfodByDefault = + HTTPClient::isAvailable() && + !ExitOnErr(getDefaultDebuginfodUrls()).empty(); + std::vector DebugFileDirectories = + InputArgs.getAllArgValues(OBJDUMP_debug_file_directory); + if (InputArgs.hasFlag(OBJDUMP_debuginfod, OBJDUMP_no_debuginfod, + ShouldUseDebuginfodByDefault)) { + HTTPClient::initialize(); + BIDFetcher = + std::make_unique(std::move(DebugFileDirectories)); + } else { + BIDFetcher = + std::make_unique(std::move(DebugFileDirectories)); + } + if (Is("otool")) parseOtoolOptions(InputArgs); else