[llvm] d033ece - [llvm-objdump] Find debug information with Build ID/debuginfod.

Daniel Thornburgh via llvm-commits llvm-commits at lists.llvm.org
Mon Oct 3 16:17:53 PDT 2022


Author: Daniel Thornburgh
Date: 2022-10-03T16:17:45-07:00
New Revision: d033ece0c985d3f89c261d030ff2ff1d9c58bbc6

URL: https://github.com/llvm/llvm-project/commit/d033ece0c985d3f89c261d030ff2ff1d9c58bbc6
DIFF: https://github.com/llvm/llvm-project/commit/d033ece0c985d3f89c261d030ff2ff1d9c58bbc6.diff

LOG: [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

Added: 
    llvm/test/tools/llvm-objdump/debuginfod.test

Modified: 
    llvm/docs/CommandGuide/llvm-objdump.rst
    llvm/docs/CommandGuide/llvm-symbolizer.rst
    llvm/test/tools/llvm-objdump/Inputs/embedded-source
    llvm/tools/llvm-objdump/CMakeLists.txt
    llvm/tools/llvm-objdump/ObjdumpOpts.td
    llvm/tools/llvm-objdump/llvm-objdump.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/docs/CommandGuide/llvm-objdump.rst b/llvm/docs/CommandGuide/llvm-objdump.rst
index 9f3fba4bf0df..07aad86de4a2 100644
--- a/llvm/docs/CommandGuide/llvm-objdump.rst
+++ b/llvm/docs/CommandGuide/llvm-objdump.rst
@@ -129,6 +129,19 @@ OPTIONS
 
   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>
 
   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
index abb174c7579e..fa4f2a0da341 100644
--- a/llvm/docs/CommandGuide/llvm-symbolizer.rst
+++ b/llvm/docs/CommandGuide/llvm-symbolizer.rst
@@ -221,6 +221,12 @@ OPTIONS
   ``auto``, which detects whether standard output supports color. Specifying
   ``--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
 
   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 8274a6619ed7..f4fa001cf731
Binary files a/llvm/test/tools/llvm-objdump/Inputs/embedded-source and b/llvm/test/tools/llvm-objdump/Inputs/embedded-source 
diff er

diff  --git a/llvm/test/tools/llvm-objdump/debuginfod.test b/llvm/test/tools/llvm-objdump/debuginfod.test
new file mode 100644
index 000000000000..2910c2068dd1
--- /dev/null
+++ b/llvm/test/tools/llvm-objdump/debuginfod.test
@@ -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[]) {

diff  --git a/llvm/tools/llvm-objdump/CMakeLists.txt b/llvm/tools/llvm-objdump/CMakeLists.txt
index 2179728e4a8c..a6c282bc24a4 100644
--- a/llvm/tools/llvm-objdump/CMakeLists.txt
+++ b/llvm/tools/llvm-objdump/CMakeLists.txt
@@ -36,6 +36,8 @@ add_llvm_tool(llvm-objdump
   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
index 151769e10902..66c39c01f0c0 100644
--- 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<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> {
   def NAME : Separate<["--"], 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 : 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">,
   HelpText<"Disassemble all executable sections found in the input files">;
 def : Flag<["-"], "d">, Alias<disassemble>, HelpText<"Alias for --disassemble">;

diff  --git a/llvm/tools/llvm-objdump/llvm-objdump.cpp b/llvm/tools/llvm-objdump/llvm-objdump.cpp
index 0977598971af..8daeb4e30fcc 100644
--- 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 @@ static StringSet<> DisasmSymbolSet;
 StringSet<> objdump::FoundSectionSet;
 static StringRef ToolName;
 
+std::unique_ptr<BuildIDFetcher> BIDFetcher;
+ExitOnError ExitOnErr;
+
 namespace {
 struct FilterResult {
   // True if the section should not be skipped.
@@ -1258,6 +1265,24 @@ static void createFakeELFSections(ObjectFile &Obj) {
     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,
                               MCContext &Ctx, MCDisassembler *PrimaryDisAsm,
                               MCDisassembler *SecondaryDisAsm,
@@ -2043,7 +2068,21 @@ static void disassembleObject(ObjectFile *Obj, bool InlineRelocs) {
   IP->setMCInstrAnalysis(MIA.get());
 
   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)
     if (!IP->applyTargetSpecificCLOption(Opt))
@@ -3080,6 +3119,22 @@ int main(int argc, char **argv) {
     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"))
     parseOtoolOptions(InputArgs);
   else


        


More information about the llvm-commits mailing list