[llvm] [symbolizer] Add a --pdb option. (PR #171053)
Yuxuan Shui via llvm-commits
llvm-commits at lists.llvm.org
Sun Dec 7 12:04:18 PST 2025
https://github.com/yshui updated https://github.com/llvm/llvm-project/pull/171053
>From e9daea126d1f8d774bb0801fe62d7633b9123a3f Mon Sep 17 00:00:00 2001
From: Yuxuan Shui <yshuiv7 at gmail.com>
Date: Sun, 7 Dec 2025 19:50:12 +0000
Subject: [PATCH] [symbolizer] Add a --pdb option.
Closes #142490
---
llvm/docs/CommandGuide/llvm-symbolizer.rst | 5 +
.../llvm/DebugInfo/Symbolize/Symbolize.h | 1 +
llvm/lib/DebugInfo/Symbolize/Symbolize.cpp | 94 +++++++++++--------
llvm/test/tools/llvm-symbolizer/pdb/pdb.test | 7 ++
llvm/tools/llvm-symbolizer/Opts.td | 1 +
.../tools/llvm-symbolizer/llvm-symbolizer.cpp | 1 +
6 files changed, 69 insertions(+), 40 deletions(-)
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..5ffdb94df9c01 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);
More information about the llvm-commits
mailing list