https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/203334
>From 012f0712d1c750ed961ccc8e7fe09fa149e99d13 Mon Sep 17 00:00:00 2001 From: Charles Zablit <[email protected]> Date: Fri, 12 Jun 2026 14:58:27 +0100 Subject: [PATCH] [lldb][Windows] Synchronize on LOAD_DLL_DEBUG_EVENT in lldb-server --- .../Process/Windows/Common/DebuggerThread.cpp | 27 ++++++++ .../Process/Windows/Common/DebuggerThread.h | 18 ++++++ .../Windows/Common/NativeProcessWindows.cpp | 61 ++++++++++++++++++- .../Windows/Common/NativeProcessWindows.h | 20 +++++- .../GDBRemoteCommunicationServerLLGS.cpp | 5 ++ 5 files changed, 128 insertions(+), 3 deletions(-) diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp index ce679d905a734..548107b67b7d6 100644 --- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.cpp @@ -208,6 +208,8 @@ Status DebuggerThread::StopDebugging(bool terminate) { ContinueAsyncException(ExceptionResult::MaskException); } + ContinueAsyncDllEvent(); + if (!terminate) { // Indicate that we want to detach. m_pid_to_detach = GetProcess().GetProcessId(); @@ -249,6 +251,31 @@ void DebuggerThread::ContinueAsyncException(ExceptionResult result) { m_exception_pred.SetValue(result, eBroadcastAlways); } +void DebuggerThread::ContinueAsyncDllEvent() { + Log *log = GetLog(WindowsLog::Process | WindowsLog::Event); + LLDB_LOG(log, "releasing parked DLL event for inferior process {0}.", + m_process.GetProcessId()); + + m_dll_event_pred.SetValue(true, eBroadcastAlways); +} + +void DebuggerThread::WaitForResumeAfterDllEvent() { + Log *log = GetLog(WindowsLog::Process | WindowsLog::Event); + + m_dll_event_pred.SetValue(false, eBroadcastNever); + m_pending_dll_event.store(true); + if (m_is_shutting_down.load()) { + m_pending_dll_event.store(false); + return; + } + LLDB_LOG(log, "parking inferior {0} on DLL event until next resume", + m_process.GetProcessId()); + m_dll_event_pred.WaitForValueEqualTo(true); + m_pending_dll_event.store(false); + LLDB_LOG(log, "inferior {0} released from DLL event wait", + m_process.GetProcessId()); +} + void DebuggerThread::FreeProcessHandles() { m_process = HostProcess(); m_main_thread = HostThread(); diff --git a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h index e0a6e9207bcd7..677d594324c27 100644 --- a/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h +++ b/lldb/source/Plugins/Process/Windows/Common/DebuggerThread.h @@ -42,6 +42,18 @@ class DebuggerThread : public std::enable_shared_from_this<DebuggerThread> { void ContinueAsyncException(ExceptionResult result); + /// Whether HandleLoadDllEvent / HandleUnloadDllEvent is currently parked + /// waiting for ContinueAsyncDllEvent. + bool HasPendingDllEvent() const { return m_pending_dll_event; } + + /// Block the current debugger thread until ContinueAsyncDllEvent is + /// invoked. + void WaitForResumeAfterDllEvent(); + + /// Release a HandleLoadDllEvent / HandleUnloadDllEvent that is waiting on + /// the DLL-event predicate. + void ContinueAsyncDllEvent(); + private: void FreeProcessHandles(); void DebugLoop(); @@ -76,6 +88,12 @@ class DebuggerThread : public std::enable_shared_from_this<DebuggerThread> { // and the debug loop can be continued. Predicate<ExceptionResult> m_exception_pred; + // Predicate which gets signalled when ContinueAsyncDllEvent is called. + Predicate<bool> m_dll_event_pred; + + // True while a HandleLoadDllEvent / HandleUnloadDllEvent is parked. + std::atomic<bool> m_pending_dll_event{false}; + // An event which gets signalled by the debugger thread when it exits the // debugger loop and is detached from the inferior. HANDLE m_debugging_ended_event = nullptr; diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp index 80ecb321209ed..d022198c6c984 100644 --- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp +++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp @@ -155,6 +155,8 @@ Status NativeProcessWindows::Resume(const ResumeActionList &resume_actions) { // anything happened. m_session_data->m_debugger->ContinueAsyncException( ExceptionResult::MaskException); + } else if (m_session_data->m_debugger->HasPendingDllEvent()) { + m_session_data->m_debugger->ContinueAsyncDllEvent(); } } else { LLDB_LOG(log, "error: process {0} is in state {1}. Returning...", @@ -478,6 +480,15 @@ void NativeProcessWindows::OnDebuggerConnected(lldb::addr_t image_base) { SetArchitecture(process_info.GetArchitecture()); } + ProcessInstanceInfo info; + if (Host::GetProcessInfo(GetDebuggedProcessId(), info)) { + FileSpec exe = info.GetExecutableFile(); + if (exe) { + FileSystem::Instance().Resolve(exe); + m_loaded_modules[exe] = image_base; + } + } + // The very first one shall always be the main thread. assert(m_threads.empty()); m_threads.push_back(std::make_unique<NativeThreadWindows>( @@ -701,13 +712,59 @@ void NativeProcessWindows::OnExitThread(lldb::tid_t thread_id, void NativeProcessWindows::OnLoadDll(const ModuleSpec &module_spec, lldb::addr_t module_addr) { - m_loaded_modules.clear(); + Log *log = GetLog(WindowsLog::Process); + + if (module_spec.GetFileSpec()) { + FileSpec resolved = module_spec.GetFileSpec(); + FileSystem::Instance().Resolve(resolved); + m_loaded_modules[resolved] = module_addr; + } m_pending_library_events = true; + + if (!m_initial_stop_seen || !m_client_supports_libraries_read) + return; + + if (!m_threads.empty()) { + auto first = static_cast<NativeThreadWindows *>(m_threads[0].get()); + SetCurrentThreadID(first->GetID()); + if (first->DoStop().Fail()) + LLDB_LOG(log, "failed to suspend thread {0} on LOAD_DLL", first->GetID()); + ThreadStopInfo info; + info.reason = lldb::eStopReasonNone; + info.signo = 0; + first->SetStopReason(info, ""); + } + SetState(eStateStopped, true); + + m_session_data->m_debugger->WaitForResumeAfterDllEvent(); } void NativeProcessWindows::OnUnloadDll(lldb::addr_t module_addr) { - m_loaded_modules.clear(); + Log *log = GetLog(WindowsLog::Process); + for (auto it = m_loaded_modules.begin(); it != m_loaded_modules.end();) { + if (it->second == module_addr) + it = m_loaded_modules.erase(it); + else + ++it; + } m_pending_library_events = true; + + if (!m_initial_stop_seen || m_client_supports_libraries_read) + return; + + if (!m_threads.empty()) { + auto first = static_cast<NativeThreadWindows *>(m_threads[0].get()); + SetCurrentThreadID(first->GetID()); + if (first->DoStop().Fail()) + LLDB_LOG(log, "failed to suspend thread {0} on UNLOAD_DLL", + first->GetID()); + ThreadStopInfo info; + info.reason = lldb::eStopReasonNone; + info.signo = 0; + first->SetStopReason(info, ""); + } + SetState(eStateStopped, true); + m_session_data->m_debugger->WaitForResumeAfterDllEvent(); } 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 d1c57fc01e06b..d9efe1c23c2b7 100644 --- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h +++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h @@ -107,6 +107,20 @@ class NativeProcessWindows : public NativeProcessProtocol, bool HasPendingLibraryEvents() override; + /// Records whether the gdb-remote client advertised + /// `qXfer:libraries:read+` (or `qXfer:libraries-svr4:read+`) in its + /// qSupported reply. + void SetClientSupportsLibrariesRead(bool v) { + m_client_supports_libraries_read = v; + } + + /// Forwards to NativeProcessProtocol's bookkeeping. + void SetEnabledExtensions(Extension flags) override { + NativeProcessProtocol::SetEnabledExtensions(flags); + SetClientSupportsLibrariesRead( + bool(flags & (Extension::libraries | Extension::libraries_svr4))); + } + /// Forward bytes from the gdb-remote `I` packet into the inferior's /// ConPTY-backed stdin via `m_stdio_communication.Write` → /// `ConnectionConPTY::Write` → `WriteFile` on the parent-side STDIN @@ -161,7 +175,7 @@ class NativeProcessWindows : public NativeProcessProtocol, /// Set whenever an OS DLL load/unload event has been seen since the last stop /// reply. - bool m_pending_library_events = true; + bool m_pending_library_events = false; /// Whether we've seen the loader breakpoint that fires once per process at /// launch / attach. @@ -170,6 +184,10 @@ class NativeProcessWindows : public NativeProcessProtocol, /// Set when Halt() / Interrupt() schedules a DebugBreakProcess injection. bool m_pending_halt = false; + /// Mirrors the client-side qXfer:libraries[-svr4]:read+ qSupported feature + /// reported by GDBRemoteCommunicationServerLLGS::HandleFeatures. + bool m_client_supports_libraries_read = false; + /// PseudoConsole for the lldb-server stdio-forwarding path. std::shared_ptr<PseudoConsole> m_pty; diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp index 0f14231cadbd5..9596eaf31c4fc 100644 --- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp @@ -4496,6 +4496,11 @@ std::vector<std::string> GDBRemoteCommunicationServerLLGS::HandleFeatures( .Case("vfork-events+", Extension::vfork) .Default({}); + if (bool(plugin_features & Extension::libraries)) + m_extensions_supported |= Extension::libraries; + if (bool(plugin_features & Extension::libraries_svr4)) + m_extensions_supported |= Extension::libraries_svr4; + // We consume lldb's swbreak/hwbreak feature, but it doesn't change the // behaviour of lldb-server. We always adjust the program counter for targets // like x86 _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
