Skip to content

Conversation

@yshui
Copy link
Contributor

@yshui yshui commented Dec 7, 2025

Closes #142490

@llvmbot
Copy link
Member

llvmbot commented Dec 7, 2025

@llvm/pr-subscribers-llvm-binary-utilities

@llvm/pr-subscribers-debuginfo

Author: Yuxuan Shui (yshui)

Changes

Closes #142490


Full diff: https://github.com/llvm/llvm-project/pull/171053.diff

6 Files Affected:

  • (modified) llvm/docs/CommandGuide/llvm-symbolizer.rst (+5)
  • (modified) llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h (+1)
  • (modified) llvm/lib/DebugInfo/Symbolize/Symbolize.cpp (+54-40)
  • (modified) llvm/test/tools/llvm-symbolizer/pdb/pdb.test (+7)
  • (modified) llvm/tools/llvm-symbolizer/Opts.td (+1)
  • (modified) llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp (+1)
diff --git a/llvm/docs/CommandGuide/llvm-symbolizer.rst b/llvm/docs/CommandGuide/llvm-symbolizer.rst
index fb86a694f5d3c..409ad43da127a 100644
--- a/llvm/docs/CommandGuide/llvm-symbolizer.rst
+++ b/llvm/docs/CommandGuide/llvm-symbolizer.rst
@@ -442,6 +442,11 @@ OPTIONS
       }
     ]
 
+.. option:: --pdb <path>
+
+  Use the specified PDB file at ``<path>``, overriding the PDB info
+  contained in the COFF object.
+
 .. option:: --pretty-print, -p
 
   Print human-readable output. If :option:`--inlining` is specified, the
diff --git a/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h b/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h
index fb8f3d8af6b1b..230c57a392f0f 100644
--- a/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h
+++ b/llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h
@@ -64,6 +64,7 @@ class LLVMSymbolizer {
     std::vector<std::string> DsymHints;
     std::string FallbackDebugPath;
     std::string DWPName;
+    std::string PDBName;
     std::vector<std::string> DebugFileDirectory;
     std::vector<std::string> GsymFileDirectory;
     size_t MaxCacheSize =
diff --git a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp
index 56527719da51f..9284f1a0565cb 100644
--- a/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp
+++ b/llvm/lib/DebugInfo/Symbolize/Symbolize.cpp
@@ -662,50 +662,64 @@ LLVMSymbolizer::getOrCreateModuleInfo(StringRef ModuleName) {
   ObjectPair Objects = ObjectsOrErr.get();
 
   std::unique_ptr<DIContext> Context;
-  // If this is a COFF object containing PDB info and not containing DWARF
-  // section, use a PDBContext to symbolize. Otherwise, use DWARF.
-  // Create a DIContext to symbolize as follows:
-  // - If there is a GSYM file, create a GsymContext.
-  // - Otherwise, if this is a COFF object containing PDB info, create a
-  // PDBContext.
-  // - Otherwise, create a DWARFContext.
-  const auto GsymFile = lookUpGsymFile(BinaryName.str());
-  if (!GsymFile.empty()) {
-    auto ReaderOrErr = gsym::GsymReader::openFile(GsymFile);
-
-    if (ReaderOrErr) {
-      std::unique_ptr<gsym::GsymReader> Reader =
-          std::make_unique<gsym::GsymReader>(std::move(*ReaderOrErr));
-
-      Context = std::make_unique<gsym::GsymContext>(std::move(Reader));
+  pdb::PDB_ReaderType ReaderType =
+      Opts.UseDIA ? pdb::PDB_ReaderType::DIA : pdb::PDB_ReaderType::Native;
+  const auto *CoffObject = dyn_cast<COFFObjectFile>(Objects.first);
+
+  // First, if the user specified a pdb file on the command line, use that.
+  if (CoffObject && !Opts.PDBName.empty()) {
+    using namespace pdb;
+    std::unique_ptr<IPDBSession> Session;
+    if (auto Err = loadDataForPDB(ReaderType, Opts.PDBName, Session)) {
+      Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>());
+      return createFileError(Opts.PDBName, std::move(Err));
     }
+    Context.reset(new PDBContext(*CoffObject, std::move(Session)));
   }
+
   if (!Context) {
-    if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) {
-      const codeview::DebugInfo *DebugInfo;
-      StringRef PDBFileName;
-      auto EC = CoffObject->getDebugPDBInfo(DebugInfo, PDBFileName);
-      // Use DWARF if there're DWARF sections.
-      bool HasDwarf = llvm::any_of(
-          Objects.first->sections(), [](SectionRef Section) -> bool {
-            if (Expected<StringRef> SectionName = Section.getName())
-              return SectionName.get() == ".debug_info";
-            return false;
-          });
-      if (!EC && !HasDwarf && DebugInfo != nullptr && !PDBFileName.empty()) {
-        using namespace pdb;
-        std::unique_ptr<IPDBSession> Session;
-
-        PDB_ReaderType ReaderType =
-            Opts.UseDIA ? PDB_ReaderType::DIA : PDB_ReaderType::Native;
-        if (auto Err = loadDataForEXE(ReaderType, Objects.first->getFileName(),
-                                      Session)) {
-          Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>());
-          // Return along the PDB filename to provide more context
-          return createFileError(PDBFileName, std::move(Err));
-        }
-        Context.reset(new PDBContext(*CoffObject, std::move(Session)));
+    // If this is a COFF object containing PDB info and not containing DWARF
+    // section, use a PDBContext to symbolize. Otherwise, use DWARF.
+    // Create a DIContext to symbolize as follows:
+    // - If there is a GSYM file, create a GsymContext.
+    // - Otherwise, if this is a COFF object containing PDB info, create a
+    // PDBContext.
+    // - Otherwise, create a DWARFContext.
+    const auto GsymFile = lookUpGsymFile(BinaryName.str());
+    if (!GsymFile.empty()) {
+      auto ReaderOrErr = gsym::GsymReader::openFile(GsymFile);
+
+      if (ReaderOrErr) {
+        std::unique_ptr<gsym::GsymReader> Reader =
+            std::make_unique<gsym::GsymReader>(std::move(*ReaderOrErr));
+
+        Context = std::make_unique<gsym::GsymContext>(std::move(Reader));
+      }
+    }
+  }
+
+  if (!Context && CoffObject) {
+    const codeview::DebugInfo *DebugInfo;
+    StringRef PDBFileName;
+    auto EC = CoffObject->getDebugPDBInfo(DebugInfo, PDBFileName);
+    // Use DWARF if there're DWARF sections.
+    bool HasDwarf = llvm::any_of(
+        Objects.first->sections(), [](SectionRef Section) -> bool {
+          if (Expected<StringRef> SectionName = Section.getName())
+            return SectionName.get() == ".debug_info";
+          return false;
+        });
+    if (!EC && !HasDwarf && DebugInfo != nullptr && !PDBFileName.empty()) {
+      using namespace pdb;
+      std::unique_ptr<IPDBSession> Session;
+
+      if (auto Err = loadDataForEXE(ReaderType, Objects.first->getFileName(),
+                                    Session)) {
+        Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>());
+        // Return along the PDB filename to provide more context
+        return createFileError(PDBFileName, std::move(Err));
       }
+      Context.reset(new PDBContext(*CoffObject, std::move(Session)));
     }
   }
   if (!Context)
diff --git a/llvm/test/tools/llvm-symbolizer/pdb/pdb.test b/llvm/test/tools/llvm-symbolizer/pdb/pdb.test
index 46a1ae9814e6d..a9d79cffb7d12 100644
--- a/llvm/test/tools/llvm-symbolizer/pdb/pdb.test
+++ b/llvm/test/tools/llvm-symbolizer/pdb/pdb.test
@@ -1,3 +1,6 @@
+RUN: rm -rf %t
+RUN: mkdir -p %t
+
 RUN: echo 0x401380 > %t.input
 RUN: echo 0x401390 >> %t.input
 RUN: echo 0x4013A0 >> %t.input
@@ -11,6 +14,10 @@ RUN:    | FileCheck %s
 RUN: llvm-symbolizer --obj="%p/Inputs/test.exe" --no-demangle < %t.input \
 RUN:    | FileCheck %s --check-prefix=CHECK-NO-DEMANGLE
 
+RUN: cp %p/Inputs/test.exe %t/test.exe
+RUN: llvm-symbolizer --obj="%t/test.exe" --pdb="%p/Inputs/test.pdb" < %t.input \
+RUN:    | FileCheck %s
+
 ; Check that -dia works
 RUN: llvm-symbolizer --dia --obj="%p/Inputs/test.exe" < %t.input \
 RUN:    | FileCheck %s
diff --git a/llvm/tools/llvm-symbolizer/Opts.td b/llvm/tools/llvm-symbolizer/Opts.td
index 10f1e6dbbddf7..ddbd4471f4452 100644
--- a/llvm/tools/llvm-symbolizer/Opts.td
+++ b/llvm/tools/llvm-symbolizer/Opts.td
@@ -41,6 +41,7 @@ def functions_EQ : Joined<["--"], "functions=">, HelpText<"Print function name f
 defm gsym_file_directory : Eq<"gsym-file-directory", "Path to directory where to look for GSYM files">, MetaVarName<"<dir>">, Group<grp_gsym>;
 def help : F<"help", "Display this help">;
 defm dwp : Eq<"dwp", "Path to DWP file to be use for any split CUs">, MetaVarName<"<file>">;
+defm pdb : Eq<"pdb", "Path to PDB file">, MetaVarName<"<file>">;
 defm dsym_hint
     : Eq<"dsym-hint",
          "Path to .dSYM bundles to search for debug info for the object files">,
diff --git a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
index 4784dafeb2948..1b9fcf35cd4ef 100644
--- a/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
+++ b/llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
@@ -500,6 +500,7 @@ int llvm_symbolizer_main(int argc, char **argv, const llvm::ToolContext &) {
   Opts.DefaultArch = Args.getLastArgValue(OPT_default_arch_EQ).str();
   Opts.Demangle = Args.hasFlag(OPT_demangle, OPT_no_demangle, !IsAddr2Line);
   Opts.DWPName = Args.getLastArgValue(OPT_dwp_EQ).str();
+  Opts.PDBName = Args.getLastArgValue(OPT_pdb_EQ).str();
   Opts.FallbackDebugPath =
       Args.getLastArgValue(OPT_fallback_debug_path_EQ).str();
   Opts.GsymFileDirectory = Args.getAllArgValues(OPT_gsym_file_directory_EQ);

@github-actions
Copy link

github-actions bot commented Dec 7, 2025

✅ With the latest revision this PR passed the C/C++ code formatter.

@jh7370
Copy link
Collaborator

jh7370 commented Dec 15, 2025

@mstorsjo, can you recommend anybody to review this item? COFF PDB is outside my area of knowledge, so I don't feel comfortable reviewing it myself.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[llvm-symbolizer] allow specifying pdb file path directly on command line

3 participants