[llvm-objdump] Find debug information with Build ID/debuginfod.
Uses the library introduced in https://reviews.llvm.org/D132504 to add build ID fetching to llvm-objdump. This allows viewing source when disassembling stripped objects. Reviewed By: jhenderson Differential Revision: https://reviews.llvm.org/D131224
This commit is contained in:
parent
8c45e80298
commit
d033ece0c9
|
@ -129,6 +129,19 @@ OPTIONS
|
||||||
|
|
||||||
Demangle symbol names in the output.
|
Demangle symbol names in the output.
|
||||||
|
|
||||||
|
.. option:: --debug-file-directory <path>
|
||||||
|
|
||||||
|
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=<format>
|
.. option:: --debug-vars=<format>
|
||||||
|
|
||||||
Print the locations (in registers or memory) of source-level variables
|
Print the locations (in registers or memory) of source-level variables
|
||||||
|
|
|
@ -221,6 +221,12 @@ OPTIONS
|
||||||
``auto``, which detects whether standard output supports color. Specifying
|
``auto``, which detects whether standard output supports color. Specifying
|
||||||
``--color`` alone is equivalent to ``--color=always``.
|
``--color`` alone is equivalent to ``--color=always``.
|
||||||
|
|
||||||
|
.. option:: --debug-file-directory <path>
|
||||||
|
|
||||||
|
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
|
.. option:: --debuginfod, --no-debuginfod
|
||||||
|
|
||||||
Whether or not to try debuginfod lookups for debug binaries. Unless specified,
|
Whether or not to try debuginfod lookups for debug binaries. Unless specified,
|
||||||
|
|
Binary file not shown.
|
@ -0,0 +1,65 @@
|
||||||
|
# This test uses the local debuginfod cache to test the llvm-objdump integration
|
||||||
|
# with the debuginfod client.
|
||||||
|
REQUIRES: x86-registered-target
|
||||||
|
|
||||||
|
RUN: rm -rf %t
|
||||||
|
RUN: mkdir %t
|
||||||
|
|
||||||
|
# Produce a stripped copy of the input binary.
|
||||||
|
# Note: See embedded-source.test for details about the input file.
|
||||||
|
RUN: llvm-objcopy --strip-debug %p/Inputs/embedded-source %t/stripped
|
||||||
|
|
||||||
|
# Printing source for the stripped binary should fail.
|
||||||
|
RUN: env DEBUGINFOD_CACHE_PATH=%t llvm-objdump -d --source %t/stripped | \
|
||||||
|
RUN: FileCheck %s --check-prefix=NOTFOUND
|
||||||
|
|
||||||
|
# Use cp to write the debug binary to an appropriately-named file in the llvm
|
||||||
|
# debuginfod cache.
|
||||||
|
RUN: cp %p/Inputs/embedded-source %t/llvmcache-7361776989772977641
|
||||||
|
|
||||||
|
# Write a broken "binary" under %t/broken.
|
||||||
|
RUN: mkdir %t/broken
|
||||||
|
RUN: echo "bad" > %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[]) {
|
|
@ -36,6 +36,8 @@ add_llvm_tool(llvm-objdump
|
||||||
OtoolOptsTableGen
|
OtoolOptsTableGen
|
||||||
)
|
)
|
||||||
|
|
||||||
|
target_link_libraries(llvm-objdump PRIVATE LLVMDebuginfod)
|
||||||
|
|
||||||
if(LLVM_HAVE_LIBXAR)
|
if(LLVM_HAVE_LIBXAR)
|
||||||
target_link_libraries(llvm-objdump PRIVATE ${XAR_LIB})
|
target_link_libraries(llvm-objdump PRIVATE ${XAR_LIB})
|
||||||
endif()
|
endif()
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
include "llvm/Option/OptParser.td"
|
include "llvm/Option/OptParser.td"
|
||||||
|
|
||||||
|
multiclass B<string name, string help1, string help2> {
|
||||||
|
def NAME: Flag<["--"], name>, HelpText<help1>;
|
||||||
|
def no_ # NAME: Flag<["--"], "no-" # name>, HelpText<help2>;
|
||||||
|
}
|
||||||
|
|
||||||
multiclass Eq<string name, string help> {
|
multiclass Eq<string name, string help> {
|
||||||
def NAME : Separate<["--"], name>;
|
def NAME : Separate<["--"], name>;
|
||||||
def NAME #_eq : Joined<["--"], name #"=">,
|
def NAME #_eq : Joined<["--"], name #"=">,
|
||||||
|
@ -39,6 +44,12 @@ def : Flag<["-"], "a">, Alias<archive_headers>,
|
||||||
def demangle : Flag<["--"], "demangle">, HelpText<"Demangle symbol names">;
|
def demangle : Flag<["--"], "demangle">, HelpText<"Demangle symbol names">;
|
||||||
def : Flag<["-"], "C">, Alias<demangle>, HelpText<"Alias for --demangle">;
|
def : Flag<["-"], "C">, Alias<demangle>, HelpText<"Alias for --demangle">;
|
||||||
|
|
||||||
|
defm debug_file_directory :
|
||||||
|
Eq<"debug-file-directory", "Path to directory where to look for debug files">,
|
||||||
|
MetaVarName<"<dir>">;
|
||||||
|
|
||||||
|
defm debuginfod : B<"debuginfod", "Use debuginfod to find debug files", "Don't use debuginfod to find debug files">;
|
||||||
|
|
||||||
def disassemble : Flag<["--"], "disassemble">,
|
def disassemble : Flag<["--"], "disassemble">,
|
||||||
HelpText<"Disassemble all executable sections found in the input files">;
|
HelpText<"Disassemble all executable sections found in the input files">;
|
||||||
def : Flag<["-"], "d">, Alias<disassemble>, HelpText<"Alias for --disassemble">;
|
def : Flag<["-"], "d">, Alias<disassemble>, HelpText<"Alias for --disassemble">;
|
||||||
|
|
|
@ -36,6 +36,9 @@
|
||||||
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
|
||||||
#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
|
#include "llvm/DebugInfo/Symbolize/SymbolizableModule.h"
|
||||||
#include "llvm/DebugInfo/Symbolize/Symbolize.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/Demangle/Demangle.h"
|
||||||
#include "llvm/MC/MCAsmInfo.h"
|
#include "llvm/MC/MCAsmInfo.h"
|
||||||
#include "llvm/MC/MCContext.h"
|
#include "llvm/MC/MCContext.h"
|
||||||
|
@ -51,6 +54,7 @@
|
||||||
#include "llvm/MC/MCTargetOptions.h"
|
#include "llvm/MC/MCTargetOptions.h"
|
||||||
#include "llvm/MC/TargetRegistry.h"
|
#include "llvm/MC/TargetRegistry.h"
|
||||||
#include "llvm/Object/Archive.h"
|
#include "llvm/Object/Archive.h"
|
||||||
|
#include "llvm/Object/BuildID.h"
|
||||||
#include "llvm/Object/COFF.h"
|
#include "llvm/Object/COFF.h"
|
||||||
#include "llvm/Object/COFFImportFile.h"
|
#include "llvm/Object/COFFImportFile.h"
|
||||||
#include "llvm/Object/ELFObjectFile.h"
|
#include "llvm/Object/ELFObjectFile.h"
|
||||||
|
@ -233,6 +237,9 @@ static StringSet<> DisasmSymbolSet;
|
||||||
StringSet<> objdump::FoundSectionSet;
|
StringSet<> objdump::FoundSectionSet;
|
||||||
static StringRef ToolName;
|
static StringRef ToolName;
|
||||||
|
|
||||||
|
std::unique_ptr<BuildIDFetcher> BIDFetcher;
|
||||||
|
ExitOnError ExitOnErr;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
struct FilterResult {
|
struct FilterResult {
|
||||||
// True if the section should not be skipped.
|
// True if the section should not be skipped.
|
||||||
|
@ -1258,6 +1265,24 @@ static void createFakeELFSections(ObjectFile &Obj) {
|
||||||
llvm_unreachable("Unsupported binary format");
|
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<OwningBinary<Binary>>
|
||||||
|
fetchBinaryByBuildID(const ObjectFile &Obj) {
|
||||||
|
Optional<object::BuildIDRef> BuildID = getBuildID(&Obj);
|
||||||
|
if (!BuildID)
|
||||||
|
return None;
|
||||||
|
Optional<std::string> Path = BIDFetcher->fetch(*BuildID);
|
||||||
|
if (!Path)
|
||||||
|
return None;
|
||||||
|
Expected<OwningBinary<Binary>> DebugBinary = createBinary(*Path);
|
||||||
|
if (!DebugBinary) {
|
||||||
|
reportWarning(toString(DebugBinary.takeError()), *Path);
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
return std::move(*DebugBinary);
|
||||||
|
}
|
||||||
|
|
||||||
static void disassembleObject(const Target *TheTarget, ObjectFile &Obj,
|
static void disassembleObject(const Target *TheTarget, ObjectFile &Obj,
|
||||||
MCContext &Ctx, MCDisassembler *PrimaryDisAsm,
|
MCContext &Ctx, MCDisassembler *PrimaryDisAsm,
|
||||||
MCDisassembler *SecondaryDisAsm,
|
MCDisassembler *SecondaryDisAsm,
|
||||||
|
@ -2043,7 +2068,21 @@ static void disassembleObject(ObjectFile *Obj, bool InlineRelocs) {
|
||||||
IP->setMCInstrAnalysis(MIA.get());
|
IP->setMCInstrAnalysis(MIA.get());
|
||||||
|
|
||||||
PrettyPrinter &PIP = selectPrettyPrinter(Triple(TripleName));
|
PrettyPrinter &PIP = selectPrettyPrinter(Triple(TripleName));
|
||||||
SourcePrinter SP(Obj, TheTarget->getName());
|
ObjectFile *DbgObj = Obj;
|
||||||
|
OwningBinary<Binary> DebugBinary;
|
||||||
|
if (!Obj->hasDebugInfo()) {
|
||||||
|
if (Optional<OwningBinary<Binary>> DebugBinaryOpt =
|
||||||
|
fetchBinaryByBuildID(*Obj)) {
|
||||||
|
if (ObjectFile *FetchedObj =
|
||||||
|
dyn_cast<ObjectFile>(DebugBinaryOpt->getBinary())) {
|
||||||
|
if (FetchedObj->hasDebugInfo()) {
|
||||||
|
DebugBinary = std::move(*DebugBinaryOpt);
|
||||||
|
DbgObj = FetchedObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SourcePrinter SP(DbgObj, TheTarget->getName());
|
||||||
|
|
||||||
for (StringRef Opt : DisassemblerOptions)
|
for (StringRef Opt : DisassemblerOptions)
|
||||||
if (!IP->applyTargetSpecificCLOption(Opt))
|
if (!IP->applyTargetSpecificCLOption(Opt))
|
||||||
|
@ -3080,6 +3119,22 @@ int main(int argc, char **argv) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize debuginfod.
|
||||||
|
const bool ShouldUseDebuginfodByDefault =
|
||||||
|
HTTPClient::isAvailable() &&
|
||||||
|
!ExitOnErr(getDefaultDebuginfodUrls()).empty();
|
||||||
|
std::vector<std::string> DebugFileDirectories =
|
||||||
|
InputArgs.getAllArgValues(OBJDUMP_debug_file_directory);
|
||||||
|
if (InputArgs.hasFlag(OBJDUMP_debuginfod, OBJDUMP_no_debuginfod,
|
||||||
|
ShouldUseDebuginfodByDefault)) {
|
||||||
|
HTTPClient::initialize();
|
||||||
|
BIDFetcher =
|
||||||
|
std::make_unique<DebuginfodFetcher>(std::move(DebugFileDirectories));
|
||||||
|
} else {
|
||||||
|
BIDFetcher =
|
||||||
|
std::make_unique<BuildIDFetcher>(std::move(DebugFileDirectories));
|
||||||
|
}
|
||||||
|
|
||||||
if (Is("otool"))
|
if (Is("otool"))
|
||||||
parseOtoolOptions(InputArgs);
|
parseOtoolOptions(InputArgs);
|
||||||
else
|
else
|
||||||
|
|
Loading…
Reference in New Issue