[Lldb-commits] [lldb] [lldb][windows] Plumb Windows DLL load/unload through lldb-server (PR #197901)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Fri May 15 07:13:52 PDT 2026
https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/197901
>From c4df8e0c32f0469d0a8ced0f8f171c51acd8ab08 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Fri, 15 May 2026 12:25:25 +0200
Subject: [PATCH 1/2] [lldb][windows] Plumb Windows DLL load/unload through
lldb-server
---
.../lldb/Host/common/NativeProcessProtocol.h | 21 ++++++++++++-
.../Windows/Common/NativeProcessWindows.cpp | 31 ++++++++++++++++---
.../Windows/Common/NativeProcessWindows.h | 12 +++++++
.../GDBRemoteCommunicationServerLLGS.cpp | 23 ++++++++++++++
4 files changed, 81 insertions(+), 6 deletions(-)
diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index b9c0120016a2d..3b133bd218316 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -45,6 +45,13 @@ struct SVR4LibraryInfo {
lldb::addr_t next;
};
+/// Generic loaded-library entry used by the non-SVR4 `qXfer:libraries:read`
+/// form of the GDB remote library-list protocol (PE on Windows).
+struct LoadedLibraryInfo {
+ std::string name;
+ lldb::addr_t base_addr;
+};
+
// NativeProcessProtocol
class NativeProcessProtocol {
public:
@@ -146,6 +153,17 @@ class NativeProcessProtocol {
"Not implemented");
}
+ /// Return the currently loaded libraries of the target in the
+ /// `qXfer:libraries:read` form (generic name + base address pairs; used on
+ /// Windows, where the inferior is not SVR4 and the module list comes from
+ /// the PE loader).
+ virtual llvm::Expected<std::vector<LoadedLibraryInfo>> GetLoadedLibraries() {
+ return llvm::createStringError(llvm::inconvertibleErrorCode(),
+ "Not implemented");
+ }
+
+ virtual bool HasPendingLibraryEvents() { return false; }
+
virtual bool IsAlive() const;
virtual size_t UpdateThreads() = 0;
@@ -268,8 +286,9 @@ class NativeProcessProtocol {
memory_tagging = (1u << 6),
savecore = (1u << 7),
siginfo_read = (1u << 8),
+ libraries = (1u << 9),
- LLVM_MARK_AS_BITMASK_ENUM(siginfo_read)
+ LLVM_MARK_AS_BITMASK_ENUM(libraries)
};
class Manager {
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
index 4fdb286c93e1e..b6985116f9ed6 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
@@ -96,6 +96,8 @@ Status NativeProcessWindows::Resume(const ResumeActionList &resume_actions) {
GetDebuggedProcessId(), state);
LLDB_LOG(log, "resuming {0} threads.", m_threads.size());
+ m_pending_library_events = false;
+
bool failed = false;
for (uint32_t i = 0; i < m_threads.size(); ++i) {
auto thread = static_cast<NativeThreadWindows *>(m_threads[i].get());
@@ -408,6 +410,26 @@ NativeProcessWindows::GetFileLoadAddress(const llvm::StringRef &file_name,
file_spec.GetPath().c_str(), GetID());
}
+llvm::Expected<std::vector<LoadedLibraryInfo>>
+NativeProcessWindows::GetLoadedLibraries() {
+ if (Status error = CacheLoadedModules(); error.Fail())
+ return error.ToError();
+
+ std::vector<LoadedLibraryInfo> libs;
+ libs.reserve(m_loaded_modules.size());
+ for (const auto &[file_spec, base] : m_loaded_modules) {
+ LoadedLibraryInfo info;
+ info.name = file_spec.GetPath();
+ info.base_addr = base;
+ libs.push_back(std::move(info));
+ }
+ return libs;
+}
+
+bool NativeProcessWindows::HasPendingLibraryEvents() {
+ return m_pending_library_events;
+}
+
void NativeProcessWindows::OnExitProcess(uint32_t exit_code) {
Log *log = GetLog(WindowsLog::Process);
LLDB_LOG(log, "Process {0} exited with code {1}", GetID(), exit_code);
@@ -641,14 +663,13 @@ void NativeProcessWindows::OnExitThread(lldb::tid_t thread_id,
void NativeProcessWindows::OnLoadDll(const ModuleSpec &module_spec,
lldb::addr_t module_addr) {
- // Simply invalidate the cached loaded modules.
- if (!m_loaded_modules.empty())
- m_loaded_modules.clear();
+ m_loaded_modules.clear();
+ m_pending_library_events = true;
}
void NativeProcessWindows::OnUnloadDll(lldb::addr_t module_addr) {
- if (!m_loaded_modules.empty())
- m_loaded_modules.clear();
+ m_loaded_modules.clear();
+ m_pending_library_events = true;
}
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
index cfba787a3d220..83451240cdac9 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
@@ -41,6 +41,10 @@ class NativeProcessWindows : public NativeProcessProtocol,
llvm::Expected<std::unique_ptr<NativeProcessProtocol>>
Attach(lldb::pid_t pid, NativeDelegate &native_delegate) override;
+
+ Extension GetSupportedExtensions() const override {
+ return Extension::libraries;
+ }
};
Status Resume(const ResumeActionList &resume_actions) override;
@@ -95,6 +99,10 @@ class NativeProcessWindows : public NativeProcessProtocol,
Status GetFileLoadAddress(const llvm::StringRef &file_name,
lldb::addr_t &load_addr) override;
+ llvm::Expected<std::vector<LoadedLibraryInfo>> GetLoadedLibraries() override;
+
+ bool HasPendingLibraryEvents() override;
+
// ProcessDebugger Overrides
void OnExitProcess(uint32_t exit_code) override;
void OnDebuggerConnected(lldb::addr_t image_base) override;
@@ -134,6 +142,10 @@ class NativeProcessWindows : public NativeProcessProtocol,
Status CacheLoadedModules();
std::map<lldb_private::FileSpec, lldb::addr_t> m_loaded_modules;
+
+ /// Set whenever an OS DLL load/unload event has been seen since the last stop
+ /// reply.
+ bool m_pending_library_events = true;
};
//------------------------------------------------------------------
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index e92d18ba8731a..e283e523c4df3 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -1054,6 +1054,9 @@ GDBRemoteCommunicationServerLLGS::PrepareStopReplyPacketForThread(
tid_stop_info.details.fork.child_tid);
}
+ if (process.HasPendingLibraryEvents())
+ response.PutCString("library:1;");
+
return response;
}
@@ -3386,6 +3389,24 @@ GDBRemoteCommunicationServerLLGS::ReadXferObject(llvm::StringRef object,
return MemoryBuffer::getMemBufferCopy(response.GetString(), __FUNCTION__);
}
+ if (object == "libraries") {
+ auto library_list = m_current_process->GetLoadedLibraries();
+ if (!library_list)
+ return library_list.takeError();
+
+ StreamString response;
+ response.Printf("<library-list>");
+ for (auto const &library : *library_list) {
+ response.Printf("<library name=\"%s\">",
+ XMLEncodeAttributeValue(library.name.c_str()).c_str());
+ response.Printf("<section address=\"0x%" PRIx64 "\"/>",
+ library.base_addr);
+ response.Printf("</library>");
+ }
+ response.Printf("</library-list>");
+ return MemoryBuffer::getMemBufferCopy(response.GetString(), __FUNCTION__);
+ }
+
if (object == "features" && annex == "target.xml")
return BuildTargetXml();
@@ -4401,6 +4422,8 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures(
ret.push_back("qXfer:auxv:read+");
if (bool(plugin_features & Extension::libraries_svr4))
ret.push_back("qXfer:libraries-svr4:read+");
+ if (bool(plugin_features & Extension::libraries))
+ ret.push_back("qXfer:libraries:read+");
if (bool(plugin_features & Extension::siginfo_read))
ret.push_back("qXfer:siginfo:read+");
if (bool(plugin_features & Extension::memory_tagging))
>From 033b365256d4149a9652fb4918f192842a76986f Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Fri, 15 May 2026 16:09:38 +0200
Subject: [PATCH 2/2] Don't re-set the executable module if it's unchanged
---
lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 8f55277f4a788..cb9cf1733f9cf 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -5537,6 +5537,9 @@ llvm::Error ProcessGDBRemote::LoadModules() {
if (obj->GetType() != ObjectFile::Type::eTypeExecutable)
return IterationAction::Continue;
+ if (target.GetExecutableModulePointer() == module_sp.get())
+ return IterationAction::Stop;
+
lldb::ModuleSP module_copy_sp = module_sp;
target.SetExecutableModule(module_copy_sp, eLoadDependentsNo);
return IterationAction::Stop;
More information about the lldb-commits
mailing list