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?= <[email protected]> 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?= <[email protected]> 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?= <[email protected]> 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) _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
