[Lldb-commits] [lldb] [lldb] Initial plugin and test for SymbolLocatorSymStore (PR #183089)
Stefan Gränitz via lldb-commits
lldb-commits at lists.llvm.org
Tue Feb 24 08:11:32 PST 2026
https://github.com/weliveindetail updated https://github.com/llvm/llvm-project/pull/183089
>From 1fe7ee48fb335e52428a9f6f94618d14a669d3a7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Mon, 23 Feb 2026 13:52:42 +0100
Subject: [PATCH 1/3] [lldb] Initial infra for SymbolLocator plugin and test
---
.../ObjectFile/PECOFF/ObjectFilePECOFF.cpp | 12 ++
.../ObjectFile/PECOFF/ObjectFilePECOFF.h | 2 +
.../Plugins/SymbolLocator/CMakeLists.txt | 1 +
.../SymbolLocator/Microsoft/CMakeLists.txt | 20 +++
.../Microsoft/SymbolLocatorMicrosoft.cpp | 165 ++++++++++++++++++
.../Microsoft/SymbolLocatorMicrosoft.h | 47 +++++
.../SymbolLocatorMicrosoftProperties.td | 7 +
.../PECOFF/SymbolVendorPECOFF.cpp | 4 +
lldb/test/API/microsoft_symsrv/Makefile | 2 +
.../microsoft_symsrv/TestMicrosoftSymSrv.py | 142 +++++++++++++++
lldb/test/API/microsoft_symsrv/main.c | 5 +
11 files changed, 407 insertions(+)
create mode 100644 lldb/source/Plugins/SymbolLocator/Microsoft/CMakeLists.txt
create mode 100644 lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.cpp
create mode 100644 lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.h
create mode 100644 lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoftProperties.td
create mode 100644 lldb/test/API/microsoft_symsrv/Makefile
create mode 100644 lldb/test/API/microsoft_symsrv/TestMicrosoftSymSrv.py
create mode 100644 lldb/test/API/microsoft_symsrv/main.c
diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
index 3a17b4c46a788..c85908f7ae34b 100644
--- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
+++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
@@ -1108,6 +1108,18 @@ std::optional<FileSpec> ObjectFilePECOFF::GetDebugLink() {
return std::nullopt;
}
+std::optional<FileSpec> ObjectFilePECOFF::GetPDBPath() {
+ if (!m_binary)
+ return std::nullopt;
+ const llvm::codeview::DebugInfo *pdb_info = nullptr;
+ llvm::StringRef pdb_file;
+ if (!m_binary->getDebugPDBInfo(pdb_info, pdb_file) && pdb_info &&
+ pdb_info->PDB70.CVSignature == llvm::OMF::Signature::PDB70 &&
+ !pdb_file.empty())
+ return FileSpec(pdb_file);
+ return std::nullopt;
+}
+
uint32_t ObjectFilePECOFF::ParseDependentModules() {
ModuleSP module_sp(GetModule());
if (!module_sp)
diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
index 8002e70e604bb..30bd672dc68f8 100644
--- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
+++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.h
@@ -130,6 +130,8 @@ class ObjectFilePECOFF : public lldb_private::ObjectFile {
/// contains it.
std::optional<lldb_private::FileSpec> GetDebugLink();
+ std::optional<lldb_private::FileSpec> GetPDBPath();
+
uint32_t GetDependentModules(lldb_private::FileSpecList &files) override;
lldb_private::Address GetEntryPointAddress() override;
diff --git a/lldb/source/Plugins/SymbolLocator/CMakeLists.txt b/lldb/source/Plugins/SymbolLocator/CMakeLists.txt
index 3b466f71dca58..818a2e18fe4fb 100644
--- a/lldb/source/Plugins/SymbolLocator/CMakeLists.txt
+++ b/lldb/source/Plugins/SymbolLocator/CMakeLists.txt
@@ -6,6 +6,7 @@ set_property(DIRECTORY PROPERTY LLDB_PLUGIN_KIND SymbolLocator)
# prevents an unstripped binary from being requested from the Debuginfod
# provider.
add_subdirectory(Debuginfod)
+add_subdirectory(Microsoft)
add_subdirectory(Default)
if (CMAKE_SYSTEM_NAME MATCHES "Darwin")
add_subdirectory(DebugSymbols)
diff --git a/lldb/source/Plugins/SymbolLocator/Microsoft/CMakeLists.txt b/lldb/source/Plugins/SymbolLocator/Microsoft/CMakeLists.txt
new file mode 100644
index 0000000000000..a27ce8aa9978e
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/Microsoft/CMakeLists.txt
@@ -0,0 +1,20 @@
+lldb_tablegen(SymbolLocatorMicrosoftProperties.inc -gen-lldb-property-defs
+ SOURCE SymbolLocatorMicrosoftProperties.td
+ TARGET LLDBPluginSymbolLocatorMicrosoftPropertiesGen)
+
+lldb_tablegen(SymbolLocatorMicrosoftPropertiesEnum.inc -gen-lldb-property-enum-defs
+ SOURCE SymbolLocatorMicrosoftProperties.td
+ TARGET LLDBPluginSymbolLocatorMicrosoftPropertiesEnumGen)
+
+add_lldb_library(lldbPluginSymbolLocatorMicrosoft PLUGIN
+ SymbolLocatorMicrosoft.cpp
+
+ LINK_LIBS
+ lldbCore
+ lldbHost
+ lldbSymbol
+ )
+
+add_dependencies(lldbPluginSymbolLocatorMicrosoft
+ LLDBPluginSymbolLocatorMicrosoftPropertiesGen
+ LLDBPluginSymbolLocatorMicrosoftPropertiesEnumGen)
diff --git a/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.cpp b/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.cpp
new file mode 100644
index 0000000000000..b988f9c0be665
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.cpp
@@ -0,0 +1,165 @@
+//===-- SymbolLocatorMicrosoft.cpp ---------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "SymbolLocatorMicrosoft.h"
+
+#include "lldb/Core/ModuleList.h"
+#include "lldb/Core/PluginManager.h"
+#include "lldb/Host/FileSystem.h"
+#include "lldb/Interpreter/OptionValueString.h"
+#include "lldb/Utility/Args.h"
+#include "lldb/Utility/LLDBLog.h"
+#include "lldb/Utility/Log.h"
+#include "lldb/Utility/UUID.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Endian.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+using namespace lldb;
+using namespace lldb_private;
+
+LLDB_PLUGIN_DEFINE(SymbolLocatorMicrosoft)
+
+namespace {
+
+#define LLDB_PROPERTIES_symbollocatormicrosoft
+#include "SymbolLocatorMicrosoftProperties.inc"
+
+enum {
+#define LLDB_PROPERTIES_symbollocatormicrosoft
+#include "SymbolLocatorMicrosoftPropertiesEnum.inc"
+};
+
+class PluginProperties : public Properties {
+public:
+ static llvm::StringRef GetSettingName() {
+ return SymbolLocatorMicrosoft::GetPluginNameStatic();
+ }
+
+ PluginProperties() {
+ m_collection_sp = std::make_shared<OptionValueProperties>(GetSettingName());
+ m_collection_sp->Initialize(g_symbollocatormicrosoft_properties_def);
+ }
+
+ Args GetURLs() const {
+ Args urls;
+ m_collection_sp->GetPropertyAtIndexAsArgs(ePropertySymStoreURLs, urls);
+ return urls;
+ }
+};
+
+} // namespace
+
+static PluginProperties &GetGlobalPluginProperties() {
+ static PluginProperties g_settings;
+ return g_settings;
+}
+
+SymbolLocatorMicrosoft::SymbolLocatorMicrosoft() : SymbolLocator() {}
+
+void SymbolLocatorMicrosoft::Initialize() {
+ static llvm::once_flag g_once_flag;
+
+ llvm::call_once(g_once_flag, []() {
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
+ nullptr, LocateExecutableSymbolFile, nullptr,
+ nullptr, SymbolLocatorMicrosoft::DebuggerInitialize);
+ });
+}
+
+void SymbolLocatorMicrosoft::DebuggerInitialize(Debugger &debugger) {
+ if (!PluginManager::GetSettingForSymbolLocatorPlugin(
+ debugger, PluginProperties::GetSettingName())) {
+ const bool is_global_setting = true;
+ PluginManager::CreateSettingForSymbolLocatorPlugin(
+ debugger, GetGlobalPluginProperties().GetValueProperties(),
+ "Properties for the Microsoft Symbol Locator plug-in.",
+ is_global_setting);
+ }
+}
+
+void SymbolLocatorMicrosoft::Terminate() {
+ PluginManager::UnregisterPlugin(CreateInstance);
+}
+
+llvm::StringRef SymbolLocatorMicrosoft::GetPluginDescriptionStatic() {
+ return "Symbol locator for PDB in Microsoft SymStore";
+}
+
+SymbolLocator *SymbolLocatorMicrosoft::CreateInstance() {
+ return new SymbolLocatorMicrosoft();
+}
+
+static llvm::StringRef getFileName(const ModuleSpec &module_spec,
+ std::string url_path) {
+ // Check if the URL path requests an executable file or a symbol file
+ bool is_executable = url_path.find("debuginfo") == std::string::npos;
+ if (is_executable)
+ return module_spec.GetFileSpec().GetFilename().GetStringRef();
+ llvm::StringRef symbol_file =
+ module_spec.GetSymbolFileSpec().GetFilename().GetStringRef();
+ // Remove llvmcache- prefix and hash, keep origin file name
+ if (symbol_file.starts_with("llvmcache-")) {
+ size_t pos = symbol_file.rfind('-');
+ if (pos != llvm::StringRef::npos) {
+ symbol_file = symbol_file.substr(pos + 1);
+ }
+ }
+ return symbol_file;
+}
+
+// LLDB stores PDB identity as a 20-byte UUID:
+// bytes 0-15 GUID in big-endian canonical form
+// bytes 16-19 Age as big-endian uint32
+//
+// The symsrv key is: <GUID-uppercase-hex><decimal-age>
+// e.g. "A0586BA32F284960B536A424603C76891" (age 1)
+static std::string formatSymStoreKey(const UUID &uuid) {
+ llvm::ArrayRef<uint8_t> bytes = uuid.GetBytes();
+ uint32_t age = llvm::support::endian::read32be(bytes.data() + 16);
+ constexpr bool LowerCase = false;
+ return llvm::toHex(bytes.slice(0, 16), LowerCase) + std::to_string(age);
+}
+
+std::optional<FileSpec> SymbolLocatorMicrosoft::LocateExecutableSymbolFile(
+ const ModuleSpec &module_spec, const FileSpecList &default_search_paths) {
+ // Bail out if we don't have a valid UUID for PDB or
+ // 'symbols.enable-external-lookup' is disabled
+ const UUID &module_uuid = module_spec.GetUUID();
+ if (!module_uuid.IsValid() || module_uuid.GetBytes().size() != 20 ||
+ !ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup())
+ return {};
+
+ Log *log = GetLog(LLDBLog::Symbols);
+
+ std::string key = formatSymStoreKey(module_uuid);
+ llvm::StringRef pdb_name =
+ module_spec.GetSymbolFileSpec().GetFilename().GetStringRef();
+ if (pdb_name.empty()) {
+ LLDB_LOGV(log, "Failed to resolve symbol PDB module: PDB name empty");
+ return {};
+ }
+
+ llvm::StringRef src_dir = GetGlobalPluginProperties().GetURLs().entries().front().ref();
+ Args SymStoreURLs = GetGlobalPluginProperties().GetURLs();
+ for (const Args::ArgEntry &URL : SymStoreURLs) {
+ llvm::SmallString<256> src;
+ llvm::sys::path::append(src, src_dir, pdb_name, URL.ref(), pdb_name);
+ FileSpec src_spec(src);
+ if (!FileSystem::Instance().Exists(src_spec)) {
+ LLDB_LOGV(log, "SymbolLocatorMicrosoft: {0} not found in symstore", src);
+ continue;
+ }
+ return src_spec;
+ }
+
+ return {};
+}
diff --git a/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.h b/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.h
new file mode 100644
index 0000000000000..36bdfd4d67d9a
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.h
@@ -0,0 +1,47 @@
+//===-- SymbolLocatorMicrosoft.h -------------------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLDB_SOURCE_PLUGINS_SYMBOLLOCATOR_MICROSOFT_SYMBOLLOCATORMICROSOFT_H
+#define LLDB_SOURCE_PLUGINS_SYMBOLLOCATOR_MICROSOFT_SYMBOLLOCATORMICROSOFT_H
+
+#include "lldb/Core/Debugger.h"
+#include "lldb/Symbol/SymbolLocator.h"
+#include "lldb/lldb-private.h"
+
+namespace lldb_private {
+
+class SymbolLocatorMicrosoft : public SymbolLocator {
+public:
+ SymbolLocatorMicrosoft();
+
+ static void Initialize();
+ static void Terminate();
+ static void DebuggerInitialize(Debugger &debugger);
+
+ static llvm::StringRef GetPluginNameStatic() { return "microsoft"; }
+ static llvm::StringRef GetPluginDescriptionStatic();
+
+ static lldb_private::SymbolLocator *CreateInstance();
+
+ /// PluginInterface protocol.
+ /// \{
+ llvm::StringRef GetPluginName() override { return GetPluginNameStatic(); }
+ /// \}
+
+ // Locate the symbol file given a module specification.
+ //
+ // Locating the file should happen only on the local computer or using the
+ // current computers global settings.
+ static std::optional<FileSpec>
+ LocateExecutableSymbolFile(const ModuleSpec &module_spec,
+ const FileSpecList &default_search_paths);
+};
+
+} // namespace lldb_private
+
+#endif // LLDB_SOURCE_PLUGINS_SYMBOLLOCATOR_MICROSOFT_SYMBOLLOCATORMICROSOFT_H
diff --git a/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoftProperties.td b/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoftProperties.td
new file mode 100644
index 0000000000000..d02098536689d
--- /dev/null
+++ b/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoftProperties.td
@@ -0,0 +1,7 @@
+include "../../../../include/lldb/Core/PropertiesBase.td"
+
+let Definition = "symbollocatormicrosoft", Path = "plugin.symbol-locator.microsoft" in {
+ def SymStoreURLs : Property<"symstore-urls", "Array">,
+ ElementType<"String">,
+ Desc<"An ordered list of symstore URLs to query for symbols. This is prepended to the contents of the _NT_SYMBOL_PATH environment variable.">;
+}
diff --git a/lldb/source/Plugins/SymbolVendor/PECOFF/SymbolVendorPECOFF.cpp b/lldb/source/Plugins/SymbolVendor/PECOFF/SymbolVendorPECOFF.cpp
index 20ccfa54a106c..1f23620372db3 100644
--- a/lldb/source/Plugins/SymbolVendor/PECOFF/SymbolVendorPECOFF.cpp
+++ b/lldb/source/Plugins/SymbolVendor/PECOFF/SymbolVendorPECOFF.cpp
@@ -74,6 +74,10 @@ SymbolVendorPECOFF::CreateInstance(const lldb::ModuleSP &module_sp,
// Otherwise, try gnu_debuglink, if one exists.
if (!fspec)
fspec = obj_file->GetDebugLink().value_or(FileSpec());
+ // For MSVC PE files, fall back to the PDB path from the CodeView debug
+ // directory so that symbol locators can use the filename for server lookups.
+ if (!fspec)
+ fspec = obj_file->GetPDBPath().value_or(FileSpec());
LLDB_SCOPED_TIMERF("SymbolVendorPECOFF::CreateInstance (module = %s)",
module_sp->GetFileSpec().GetPath().c_str());
diff --git a/lldb/test/API/microsoft_symsrv/Makefile b/lldb/test/API/microsoft_symsrv/Makefile
new file mode 100644
index 0000000000000..c9319d6e6888a
--- /dev/null
+++ b/lldb/test/API/microsoft_symsrv/Makefile
@@ -0,0 +1,2 @@
+C_SOURCES := main.c
+include Makefile.rules
diff --git a/lldb/test/API/microsoft_symsrv/TestMicrosoftSymSrv.py b/lldb/test/API/microsoft_symsrv/TestMicrosoftSymSrv.py
new file mode 100644
index 0000000000000..4e778362ba713
--- /dev/null
+++ b/lldb/test/API/microsoft_symsrv/TestMicrosoftSymSrv.py
@@ -0,0 +1,142 @@
+import glob
+import os
+import shutil
+import tempfile
+
+import lldb
+from lldbsuite.test.decorators import *
+import lldbsuite.test.lldbutil as lldbutil
+from lldbsuite.test.lldbtest import *
+
+
+"""
+Test support for the Microsoft symbol server (symsrv) protocol.
+
+LLDB's SymbolLocatorMicrosoft plugin locates PDB files from symbol servers
+that follow the Microsoft symsrv directory layout:
+
+ <store>/<pdb-name>/<GUID-uppercase-no-dashes><age-decimal>/<pdb-name>
+
+The symstore-urls setting accepts entries in SRV*<cache>*<server> notation,
+matching the _NT_SYMBOL_PATH convention used by WinDbg and other Microsoft
+debuggers.
+"""
+
+import debugpy
+debugpy.listen(("127.0.0.1", 5678))
+debugpy.wait_for_client()
+debugpy.breakpoint()
+
+class MicrosoftSymSrvTests(TestBase):
+ NO_DEBUG_INFO_TESTCASE = True
+
+ @skipUnlessPlatform(["windows"])
+ def test_local_folder(self):
+ """Verify that LLDB fetches the PDB from a local SymStore directory."""
+ tmp_dir = tempfile.mkdtemp()
+ symstore_dir = self.populate_symstore(tmp_dir)
+
+ self.runCmd(
+ "settings set plugin.symbol-locator.microsoft.symstore-urls %s" %
+ symstore_dir.replace("\\", "/")
+ )
+
+ self.try_breakpoint(should_have_loc=True)
+ #shutil.rmtree(tmp_dir)
+ print(tmp_dir)
+
+ def try_breakpoint(self, should_have_loc):
+ target = self.dbg.CreateTarget(self.aout)
+ self.assertTrue(target and target.IsValid(), "Target is valid")
+
+ bp = target.BreakpointCreateByName("func")
+ self.assertTrue(bp and bp.IsValid(), "Breakpoint is valid")
+ self.assertEqual(bp.GetNumLocations(), 1)
+
+ loc = bp.GetLocationAtIndex(0)
+ self.assertTrue(loc and loc.IsValid(), "Location is valid")
+ addr = loc.GetAddress()
+ self.assertTrue(addr and addr.IsValid(), "Loc address is valid")
+ line_entry = addr.GetLineEntry()
+ self.assertEqual(
+ should_have_loc,
+ line_entry is not None and line_entry.IsValid(),
+ "Loc line entry validity",
+ )
+ if should_have_loc:
+ self.assertEqual(line_entry.GetLine(), 2)
+ self.assertEqual(
+ line_entry.GetFileSpec().GetFilename(),
+ self.main_source_file.GetFilename(),
+ )
+ self.dbg.DeleteTarget(target)
+
+ def populate_symstore(self, tmp):
+ """
+ Build test binary, mock local symstore directory tree:
+ tmp/test/a.out binary (no PDB in search path)
+ tmp/server/<pdb>/<key>/<pdb> PDB in symstore
+ """
+ self.build()
+ pdbs = glob.glob(os.path.join(self.getBuildDir(), "*.pdb"))
+ if len(pdbs) == 0:
+ self.skipTest("Build did not produce a PDB file")
+
+ self.main_source_file = lldb.SBFileSpec("main.c")
+
+ binary_name = "a.out"
+ pdb_name = "a.pdb"
+ uuid_str = self._get_uuid(binary_name)
+ if uuid_str is None:
+ self.skipTest("Could not obtain a 20-byte PDB UUID from the binary")
+
+ symsrv_key = self._uuid_to_symsrv_key(uuid_str)
+
+ # Set up test directory with just the binary (no PDB).
+ test_dir = os.path.join(tmp, "test")
+ os.makedirs(test_dir)
+ shutil.move(self.getBuildArtifact(binary_name), test_dir)
+ self.aout = os.path.join(test_dir, binary_name)
+
+ # SymStore directory tree: <pdb>/<key>/<pdb>
+ server_dir = os.path.join(tmp, "server")
+ pdb_key_dir = os.path.join(server_dir, pdb_name, symsrv_key)
+ os.makedirs(pdb_key_dir)
+ shutil.move(
+ self.getBuildArtifact(pdb_name),
+ os.path.join(pdb_key_dir, pdb_name),
+ )
+
+ return server_dir
+
+ # -----------------------------------------------------------------------
+ # Private helpers
+ # -----------------------------------------------------------------------
+
+ #def _get_pdb_name(self):
+ # """Return the basename of the first PDB in the build directory."""
+ # pdbs = glob.glob(os.path.join(self.getBuildDir(), "*.pdb"))
+ # return os.path.basename(pdbs[0]) if pdbs else None
+
+ def _get_uuid(self, binary_name):
+ """Return the UUID string (dashes removed, lowercase) for binary_name,
+ or None if it is not a 20-byte PDB UUID."""
+ try:
+ spec = lldb.SBModuleSpec()
+ spec.SetFileSpec(
+ lldb.SBFileSpec(self.getBuildArtifact(binary_name))
+ )
+ module = lldb.SBModule(spec)
+ raw = module.GetUUIDString().replace("-", "").lower()
+ return raw if len(raw) == 40 else None
+ except Exception:
+ return None
+
+ @staticmethod
+ def _uuid_to_symsrv_key(uuid_lower_40):
+ """Convert a 40-char lowercase hex UUID string to a Microsoft symsrv
+ key: uppercase GUID (32 chars) followed by decimal age."""
+ upper = uuid_lower_40.upper()
+ guid_hex = upper[:32]
+ age = int(upper[32:], 16)
+ return guid_hex + str(age)
diff --git a/lldb/test/API/microsoft_symsrv/main.c b/lldb/test/API/microsoft_symsrv/main.c
new file mode 100644
index 0000000000000..a95762e80ea44
--- /dev/null
+++ b/lldb/test/API/microsoft_symsrv/main.c
@@ -0,0 +1,5 @@
+int func(int argc, const char *argv[]) {
+ return (argc + 1) * (argv[argc][0] + 2);
+}
+
+int main(int argc, const char *argv[]) { return func(0, argv); }
>From add842d809b902e85b4d6cc2daad2517282761d4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Tue, 24 Feb 2026 16:15:00 +0100
Subject: [PATCH 2/3] Polish symbol locator plugin and test
---
.../ObjectFile/PECOFF/ObjectFilePECOFF.cpp | 19 ++--
.../Microsoft/SymbolLocatorMicrosoft.cpp | 91 ++++++++----------
.../PECOFF/SymbolVendorPECOFF.cpp | 7 +-
.../microsoft_symsrv/TestMicrosoftSymSrv.py | 94 ++++++-------------
4 files changed, 83 insertions(+), 128 deletions(-)
diff --git a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
index c85908f7ae34b..2e169bb6e8a53 100644
--- a/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
+++ b/lldb/source/Plugins/ObjectFile/PECOFF/ObjectFilePECOFF.cpp
@@ -1109,15 +1109,18 @@ std::optional<FileSpec> ObjectFilePECOFF::GetDebugLink() {
}
std::optional<FileSpec> ObjectFilePECOFF::GetPDBPath() {
- if (!m_binary)
- return std::nullopt;
- const llvm::codeview::DebugInfo *pdb_info = nullptr;
llvm::StringRef pdb_file;
- if (!m_binary->getDebugPDBInfo(pdb_info, pdb_file) && pdb_info &&
- pdb_info->PDB70.CVSignature == llvm::OMF::Signature::PDB70 &&
- !pdb_file.empty())
- return FileSpec(pdb_file);
- return std::nullopt;
+ const llvm::codeview::DebugInfo *pdb_info = nullptr;
+ if (llvm::Error Err = m_binary->getDebugPDBInfo(pdb_info, pdb_file)) {
+ // Ignore corrupt DebugInfo sections
+ llvm::consumeError(std::move(Err));
+ return std::nullopt;
+ }
+ if (pdb_file.empty()) {
+ // No DebugInfo section
+ return std::nullopt;
+ }
+ return FileSpec(pdb_file);
}
uint32_t ObjectFilePECOFF::ParseDependentModules() {
diff --git a/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.cpp b/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.cpp
index b988f9c0be665..549460df97d02 100644
--- a/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.cpp
+++ b/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.cpp
@@ -65,20 +65,17 @@ static PluginProperties &GetGlobalPluginProperties() {
SymbolLocatorMicrosoft::SymbolLocatorMicrosoft() : SymbolLocator() {}
void SymbolLocatorMicrosoft::Initialize() {
- static llvm::once_flag g_once_flag;
-
- llvm::call_once(g_once_flag, []() {
- PluginManager::RegisterPlugin(
- GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
- nullptr, LocateExecutableSymbolFile, nullptr,
- nullptr, SymbolLocatorMicrosoft::DebuggerInitialize);
- });
+ // First version can only locate PDB in local SymStore (no download yet)
+ PluginManager::RegisterPlugin(
+ GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
+ nullptr, LocateExecutableSymbolFile, nullptr,
+ nullptr, SymbolLocatorMicrosoft::DebuggerInitialize);
}
void SymbolLocatorMicrosoft::DebuggerInitialize(Debugger &debugger) {
if (!PluginManager::GetSettingForSymbolLocatorPlugin(
debugger, PluginProperties::GetSettingName())) {
- const bool is_global_setting = true;
+ constexpr bool is_global_setting = true;
PluginManager::CreateSettingForSymbolLocatorPlugin(
debugger, GetGlobalPluginProperties().GetValueProperties(),
"Properties for the Microsoft Symbol Locator plug-in.",
@@ -98,30 +95,13 @@ SymbolLocator *SymbolLocatorMicrosoft::CreateInstance() {
return new SymbolLocatorMicrosoft();
}
-static llvm::StringRef getFileName(const ModuleSpec &module_spec,
- std::string url_path) {
- // Check if the URL path requests an executable file or a symbol file
- bool is_executable = url_path.find("debuginfo") == std::string::npos;
- if (is_executable)
- return module_spec.GetFileSpec().GetFilename().GetStringRef();
- llvm::StringRef symbol_file =
- module_spec.GetSymbolFileSpec().GetFilename().GetStringRef();
- // Remove llvmcache- prefix and hash, keep origin file name
- if (symbol_file.starts_with("llvmcache-")) {
- size_t pos = symbol_file.rfind('-');
- if (pos != llvm::StringRef::npos) {
- symbol_file = symbol_file.substr(pos + 1);
- }
- }
- return symbol_file;
-}
-
-// LLDB stores PDB identity as a 20-byte UUID:
-// bytes 0-15 GUID in big-endian canonical form
-// bytes 16-19 Age as big-endian uint32
+// LLDB stores PDB identity as a 20-byte UUID composed of 16-byte GUID and
+// 4-byte age:
+// 12345678-1234-5678-9ABC-DEF012345678-00000001
+//
+// SymStore key is a string with no separators and age as decimal:
+// 12345678123456789ABCDEF0123456781
//
-// The symsrv key is: <GUID-uppercase-hex><decimal-age>
-// e.g. "A0586BA32F284960B536A424603C76891" (age 1)
static std::string formatSymStoreKey(const UUID &uuid) {
llvm::ArrayRef<uint8_t> bytes = uuid.GetBytes();
uint32_t age = llvm::support::endian::read32be(bytes.data() + 16);
@@ -131,34 +111,45 @@ static std::string formatSymStoreKey(const UUID &uuid) {
std::optional<FileSpec> SymbolLocatorMicrosoft::LocateExecutableSymbolFile(
const ModuleSpec &module_spec, const FileSpecList &default_search_paths) {
- // Bail out if we don't have a valid UUID for PDB or
- // 'symbols.enable-external-lookup' is disabled
- const UUID &module_uuid = module_spec.GetUUID();
- if (!module_uuid.IsValid() || module_uuid.GetBytes().size() != 20 ||
+ const UUID &uuid = module_spec.GetUUID();
+ if (!uuid.IsValid() ||
!ModuleList::GetGlobalModuleListProperties().GetEnableExternalLookup())
return {};
Log *log = GetLog(LLDBLog::Symbols);
-
- std::string key = formatSymStoreKey(module_uuid);
- llvm::StringRef pdb_name =
- module_spec.GetSymbolFileSpec().GetFilename().GetStringRef();
+ std::string pdb_name =
+ module_spec.GetSymbolFileSpec().GetFilename().GetStringRef().str();
if (pdb_name.empty()) {
LLDB_LOGV(log, "Failed to resolve symbol PDB module: PDB name empty");
return {};
}
- llvm::StringRef src_dir = GetGlobalPluginProperties().GetURLs().entries().front().ref();
- Args SymStoreURLs = GetGlobalPluginProperties().GetURLs();
- for (const Args::ArgEntry &URL : SymStoreURLs) {
- llvm::SmallString<256> src;
- llvm::sys::path::append(src, src_dir, pdb_name, URL.ref(), pdb_name);
- FileSpec src_spec(src);
- if (!FileSystem::Instance().Exists(src_spec)) {
- LLDB_LOGV(log, "SymbolLocatorMicrosoft: {0} not found in symstore", src);
- continue;
+ LLDB_LOGV(log, "LocateExecutableSymbolFile {0} with UUID {1}", pdb_name,
+ uuid.GetAsString());
+ if (uuid.GetBytes().size() != 20) {
+ LLDB_LOGV(log, " Failed to resolve symbol PDB module: UUID invalid");
+ return {};
+ }
+
+ // FIXME: We need this for the test executable, because it is loaded as DWARF
+ if (!llvm::StringRef(pdb_name).ends_with(".pdb")) {
+ auto last_dot = pdb_name.find_last_of('.');
+ if (last_dot != llvm::StringRef::npos) {
+ pdb_name = pdb_name.substr(0, last_dot);
+ }
+ pdb_name += ".pdb";
+ }
+
+ std::string key = formatSymStoreKey(uuid);
+ Args sym_store_urls = GetGlobalPluginProperties().GetURLs();
+ for (const Args::ArgEntry &url : sym_store_urls) {
+ llvm::SmallString<256> path;
+ llvm::sys::path::append(path, url.ref(), pdb_name, key, pdb_name);
+ FileSpec spec(path);
+ if (FileSystem::Instance().Exists(spec)) {
+ LLDB_LOGV(log, " Found {0} in SymStore {1}", pdb_name, url.ref());
+ return spec;
}
- return src_spec;
}
return {};
diff --git a/lldb/source/Plugins/SymbolVendor/PECOFF/SymbolVendorPECOFF.cpp b/lldb/source/Plugins/SymbolVendor/PECOFF/SymbolVendorPECOFF.cpp
index 1f23620372db3..94c3548b80e4a 100644
--- a/lldb/source/Plugins/SymbolVendor/PECOFF/SymbolVendorPECOFF.cpp
+++ b/lldb/source/Plugins/SymbolVendor/PECOFF/SymbolVendorPECOFF.cpp
@@ -71,13 +71,12 @@ SymbolVendorPECOFF::CreateInstance(const lldb::ModuleSP &module_sp,
// If the module specified a filespec, use that.
FileSpec fspec = module_sp->GetSymbolFileFileSpec();
+ // Otherwise, use the PDB path from CodeView.
+ if (!fspec)
+ fspec = obj_file->GetPDBPath().value_or(FileSpec());
// Otherwise, try gnu_debuglink, if one exists.
if (!fspec)
fspec = obj_file->GetDebugLink().value_or(FileSpec());
- // For MSVC PE files, fall back to the PDB path from the CodeView debug
- // directory so that symbol locators can use the filename for server lookups.
- if (!fspec)
- fspec = obj_file->GetPDBPath().value_or(FileSpec());
LLDB_SCOPED_TIMERF("SymbolVendorPECOFF::CreateInstance (module = %s)",
module_sp->GetFileSpec().GetPath().c_str());
diff --git a/lldb/test/API/microsoft_symsrv/TestMicrosoftSymSrv.py b/lldb/test/API/microsoft_symsrv/TestMicrosoftSymSrv.py
index 4e778362ba713..eaae4792489f0 100644
--- a/lldb/test/API/microsoft_symsrv/TestMicrosoftSymSrv.py
+++ b/lldb/test/API/microsoft_symsrv/TestMicrosoftSymSrv.py
@@ -5,46 +5,11 @@
import lldb
from lldbsuite.test.decorators import *
-import lldbsuite.test.lldbutil as lldbutil
from lldbsuite.test.lldbtest import *
-
-"""
-Test support for the Microsoft symbol server (symsrv) protocol.
-
-LLDB's SymbolLocatorMicrosoft plugin locates PDB files from symbol servers
-that follow the Microsoft symsrv directory layout:
-
- <store>/<pdb-name>/<GUID-uppercase-no-dashes><age-decimal>/<pdb-name>
-
-The symstore-urls setting accepts entries in SRV*<cache>*<server> notation,
-matching the _NT_SYMBOL_PATH convention used by WinDbg and other Microsoft
-debuggers.
-"""
-
-import debugpy
-debugpy.listen(("127.0.0.1", 5678))
-debugpy.wait_for_client()
-debugpy.breakpoint()
-
class MicrosoftSymSrvTests(TestBase):
NO_DEBUG_INFO_TESTCASE = True
- @skipUnlessPlatform(["windows"])
- def test_local_folder(self):
- """Verify that LLDB fetches the PDB from a local SymStore directory."""
- tmp_dir = tempfile.mkdtemp()
- symstore_dir = self.populate_symstore(tmp_dir)
-
- self.runCmd(
- "settings set plugin.symbol-locator.microsoft.symstore-urls %s" %
- symstore_dir.replace("\\", "/")
- )
-
- self.try_breakpoint(should_have_loc=True)
- #shutil.rmtree(tmp_dir)
- print(tmp_dir)
-
def try_breakpoint(self, should_have_loc):
target = self.dbg.CreateTarget(self.aout)
self.assertTrue(target and target.IsValid(), "Target is valid")
@@ -73,7 +38,7 @@ def try_breakpoint(self, should_have_loc):
def populate_symstore(self, tmp):
"""
- Build test binary, mock local symstore directory tree:
+ Build test binary and mock local symstore directory tree:
tmp/test/a.out binary (no PDB in search path)
tmp/server/<pdb>/<key>/<pdb> PDB in symstore
"""
@@ -86,12 +51,10 @@ def populate_symstore(self, tmp):
binary_name = "a.out"
pdb_name = "a.pdb"
- uuid_str = self._get_uuid(binary_name)
- if uuid_str is None:
+ key = self.symstore_key(binary_name)
+ if key is None:
self.skipTest("Could not obtain a 20-byte PDB UUID from the binary")
- symsrv_key = self._uuid_to_symsrv_key(uuid_str)
-
# Set up test directory with just the binary (no PDB).
test_dir = os.path.join(tmp, "test")
os.makedirs(test_dir)
@@ -100,7 +63,7 @@ def populate_symstore(self, tmp):
# SymStore directory tree: <pdb>/<key>/<pdb>
server_dir = os.path.join(tmp, "server")
- pdb_key_dir = os.path.join(server_dir, pdb_name, symsrv_key)
+ pdb_key_dir = os.path.join(server_dir, pdb_name, key)
os.makedirs(pdb_key_dir)
shutil.move(
self.getBuildArtifact(pdb_name),
@@ -109,34 +72,33 @@ def populate_symstore(self, tmp):
return server_dir
- # -----------------------------------------------------------------------
- # Private helpers
- # -----------------------------------------------------------------------
-
- #def _get_pdb_name(self):
- # """Return the basename of the first PDB in the build directory."""
- # pdbs = glob.glob(os.path.join(self.getBuildDir(), "*.pdb"))
- # return os.path.basename(pdbs[0]) if pdbs else None
-
- def _get_uuid(self, binary_name):
- """Return the UUID string (dashes removed, lowercase) for binary_name,
- or None if it is not a 20-byte PDB UUID."""
+ def symstore_key(self, exe):
+ """Load module UUID like: 12345678-1234-5678-9ABC-DEF012345678-00000001
+ and transform to SymStore key: 12345678123456789ABCDEF0123456781"""
try:
spec = lldb.SBModuleSpec()
- spec.SetFileSpec(
- lldb.SBFileSpec(self.getBuildArtifact(binary_name))
- )
+ spec.SetFileSpec(lldb.SBFileSpec(self.getBuildArtifact(exe)))
module = lldb.SBModule(spec)
- raw = module.GetUUIDString().replace("-", "").lower()
- return raw if len(raw) == 40 else None
+ raw = module.GetUUIDString().replace("-", "").upper()
+ if len(raw) != 40:
+ return None
+ guid_hex = raw[:32]
+ age = int(raw[32:], 16)
+ return guid_hex + str(age)
except Exception:
return None
- @staticmethod
- def _uuid_to_symsrv_key(uuid_lower_40):
- """Convert a 40-char lowercase hex UUID string to a Microsoft symsrv
- key: uppercase GUID (32 chars) followed by decimal age."""
- upper = uuid_lower_40.upper()
- guid_hex = upper[:32]
- age = int(upper[32:], 16)
- return guid_hex + str(age)
+ # TODO: Check on other platforms, it should work in theory
+ @skipUnlessPlatform(["windows"])
+ def test_local_folder(self):
+ """Check that LLDB can fetch PDB from local SymStore directory"""
+ tmp_dir = tempfile.mkdtemp()
+ symstore_dir = self.populate_symstore(tmp_dir)
+
+ self.runCmd(
+ "settings set plugin.symbol-locator.microsoft.symstore-urls %s" %
+ symstore_dir.replace("\\", "/")
+ )
+
+ self.try_breakpoint(should_have_loc=True)
+ shutil.rmtree(tmp_dir)
>From 4a9bec246bb435564b9bb0711e0f20fb4724287e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Stefan=20Gr=C3=A4nitz?= <stefan.graenitz at gmail.com>
Date: Tue, 24 Feb 2026 17:11:05 +0100
Subject: [PATCH 3/3] Fix formatting (NFC)
---
.../SymbolLocator/Microsoft/SymbolLocatorMicrosoft.cpp | 4 ++--
lldb/test/API/microsoft_symsrv/TestMicrosoftSymSrv.py | 9 +++++----
2 files changed, 7 insertions(+), 6 deletions(-)
diff --git a/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.cpp b/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.cpp
index 549460df97d02..9d8a018639673 100644
--- a/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.cpp
+++ b/lldb/source/Plugins/SymbolLocator/Microsoft/SymbolLocatorMicrosoft.cpp
@@ -68,8 +68,8 @@ void SymbolLocatorMicrosoft::Initialize() {
// First version can only locate PDB in local SymStore (no download yet)
PluginManager::RegisterPlugin(
GetPluginNameStatic(), GetPluginDescriptionStatic(), CreateInstance,
- nullptr, LocateExecutableSymbolFile, nullptr,
- nullptr, SymbolLocatorMicrosoft::DebuggerInitialize);
+ nullptr, LocateExecutableSymbolFile, nullptr, nullptr,
+ SymbolLocatorMicrosoft::DebuggerInitialize);
}
void SymbolLocatorMicrosoft::DebuggerInitialize(Debugger &debugger) {
diff --git a/lldb/test/API/microsoft_symsrv/TestMicrosoftSymSrv.py b/lldb/test/API/microsoft_symsrv/TestMicrosoftSymSrv.py
index eaae4792489f0..f69d4cc9791be 100644
--- a/lldb/test/API/microsoft_symsrv/TestMicrosoftSymSrv.py
+++ b/lldb/test/API/microsoft_symsrv/TestMicrosoftSymSrv.py
@@ -7,6 +7,7 @@
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
+
class MicrosoftSymSrvTests(TestBase):
NO_DEBUG_INFO_TESTCASE = True
@@ -74,7 +75,7 @@ def populate_symstore(self, tmp):
def symstore_key(self, exe):
"""Load module UUID like: 12345678-1234-5678-9ABC-DEF012345678-00000001
- and transform to SymStore key: 12345678123456789ABCDEF0123456781"""
+ and transform to SymStore key: 12345678123456789ABCDEF0123456781"""
try:
spec = lldb.SBModuleSpec()
spec.SetFileSpec(lldb.SBFileSpec(self.getBuildArtifact(exe)))
@@ -94,10 +95,10 @@ def test_local_folder(self):
"""Check that LLDB can fetch PDB from local SymStore directory"""
tmp_dir = tempfile.mkdtemp()
symstore_dir = self.populate_symstore(tmp_dir)
-
+
self.runCmd(
- "settings set plugin.symbol-locator.microsoft.symstore-urls %s" %
- symstore_dir.replace("\\", "/")
+ "settings set plugin.symbol-locator.microsoft.symstore-urls %s"
+ % symstore_dir.replace("\\", "/")
)
self.try_breakpoint(should_have_loc=True)
More information about the lldb-commits
mailing list