[Lldb-commits] [lldb] [lldb][Windows] add stdin support to lldb-server (PR #201638)

Charles Zablit via lldb-commits lldb-commits at lists.llvm.org
Mon Jun 8 09:01:54 PDT 2026


https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/201638

>From 68a6ea7ab9f0dd75ba85c0651b5b7168673c9157 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Thu, 4 Jun 2026 18:09:30 +0100
Subject: [PATCH 1/2] [lldb][Windows] add stdin support to lldb-server

---
 .../lldb/Host/common/NativeProcessProtocol.h  |  6 ++++-
 .../Host/windows/ConnectionConPTYWindows.cpp  | 27 ++++++++++++++++++-
 .../Windows/Common/NativeProcessWindows.cpp   | 15 +++++++++++
 .../Windows/Common/NativeProcessWindows.h     |  7 +++++
 .../Process/Windows/Common/ProcessWindows.cpp |  1 +
 .../GDBRemoteCommunicationServerLLGS.cpp      |  9 ++++++-
 .../Process/gdb-remote/ProcessGDBRemote.cpp   | 16 ++++++++++-
 7 files changed, 77 insertions(+), 4 deletions(-)

diff --git a/lldb/include/lldb/Host/common/NativeProcessProtocol.h b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
index 4e4804623b1d8..435185a38f3f9 100644
--- a/lldb/include/lldb/Host/common/NativeProcessProtocol.h
+++ b/lldb/include/lldb/Host/common/NativeProcessProtocol.h
@@ -247,8 +247,12 @@ class NativeProcessProtocol {
   // Access to inferior stdio
   virtual int GetTerminalFileDescriptor() { return m_terminal_fd; }
 
-  // Stop id interface
+  /// Write up to \p len bytes from \p buf to the inferior's stdin.
+  virtual size_t WriteStdin(const void *buf, size_t len, Status &error) {
+    return 0;
+  }
 
+  // Stop id interface
   uint32_t GetStopID() const;
 
   // Callbacks for low-level process state changes
diff --git a/lldb/source/Host/windows/ConnectionConPTYWindows.cpp b/lldb/source/Host/windows/ConnectionConPTYWindows.cpp
index e9d026fc7dc9f..f7e90421653e3 100644
--- a/lldb/source/Host/windows/ConnectionConPTYWindows.cpp
+++ b/lldb/source/Host/windows/ConnectionConPTYWindows.cpp
@@ -61,5 +61,30 @@ size_t ConnectionConPTY::Read(void *dst, size_t dst_len,
 size_t ConnectionConPTY::Write(const void *src, size_t src_len,
                                lldb::ConnectionStatus &status,
                                Status *error_ptr) {
-  llvm_unreachable("not implemented");
+  if (!m_pty || !m_pty->IsConnected()) {
+    status = eConnectionStatusNoConnection;
+    if (error_ptr)
+      *error_ptr = Status::FromErrorString("ConPTY not connected");
+    return 0;
+  }
+  HANDLE stdin_handle = m_pty->GetSTDINHandle();
+  if (stdin_handle == INVALID_HANDLE_VALUE || stdin_handle == nullptr) {
+    status = eConnectionStatusNoConnection;
+    if (error_ptr)
+      *error_ptr = Status::FromErrorString("ConPTY STDIN handle is invalid");
+    return 0;
+  }
+  DWORD written = 0;
+  if (!::WriteFile(stdin_handle, src, static_cast<DWORD>(src_len), &written,
+                   nullptr)) {
+    DWORD err = ::GetLastError();
+    status = (err == ERROR_BROKEN_PIPE || err == ERROR_NO_DATA)
+                 ? eConnectionStatusEndOfFile
+                 : eConnectionStatusError;
+    if (error_ptr)
+      *error_ptr = Status(err, lldb::eErrorTypeWin32);
+    return written;
+  }
+  status = eConnectionStatusSuccess;
+  return written;
 }
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
index b235ab281bad6..bf447c9ea81f5 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.cpp
@@ -749,4 +749,19 @@ void NativeProcessWindows::STDIOReadThreadBytesReceived(void *baton,
   self->m_delegate.NewProcessOutput(
       self, llvm::StringRef(static_cast<const char *>(src), src_len));
 }
+
+size_t NativeProcessWindows::WriteStdin(const void *buf, size_t len,
+                                        Status &error) {
+  if (!m_stdio_communication.HasConnection()) {
+    error = Status::FromErrorString(
+        "no ConPTY connection on this NativeProcessWindows");
+    return 0;
+  }
+  ConnectionStatus status;
+  size_t written = m_stdio_communication.Write(buf, len, status, &error);
+  if (status != eConnectionStatusSuccess && error.Success())
+    error = Status::FromErrorStringWithFormatv(
+        "ConPTY stdin write returned status {0}", static_cast<int>(status));
+  return written;
+}
 } // namespace lldb_private
diff --git a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
index 95b85754ebdeb..cac3920d82aa9 100644
--- a/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
+++ b/lldb/source/Plugins/Process/Windows/Common/NativeProcessWindows.h
@@ -107,6 +107,13 @@ class NativeProcessWindows : public NativeProcessProtocol,
 
   bool HasPendingLibraryEvents() override;
 
+  /// 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
+  /// HANDLE. Returns the number of bytes written (0 if the PTY is
+  /// disconnected or write fails).
+  size_t WriteStdin(const void *buf, size_t len, Status &error) override;
+
   // ProcessDebugger Overrides
   void OnExitProcess(uint32_t exit_code) override;
   void OnDebuggerConnected(lldb::addr_t image_base) override;
diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 30a1610ee5a17..143f779d0b15b 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -46,6 +46,7 @@
 #include "ExceptionRecord.h"
 #include "ForwardDecl.h"
 #include "LocalDebugDelegate.h"
+#include "Plugins/Process/Utility/IOHandlerProcessSTDIOWindows.h"
 #include "ProcessWindowsLog.h"
 #include "TargetThreadWindows.h"
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
index d56026e011564..077144f94227d 100644
--- a/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/GDBRemoteCommunicationServerLLGS.cpp
@@ -2569,12 +2569,19 @@ GDBRemoteCommunicationServerLLGS::Handle_I(StringExtractorGDBRemote &packet) {
     // write directly to stdin *this might block if stdin buffer is full*
     // TODO: enqueue this block in circular buffer and send window size to
     // remote host
-    ConnectionStatus status;
     Status error;
+
+#if defined(_WIN32)
+    // On Windows the inferior's stdio is owned by NativeProcessWindows.
+    if (m_current_process->WriteStdin(tmp, read, error) != read || error.Fail())
+      return SendErrorResponse(0x15);
+#else
+    ConnectionStatus status;
     m_stdio_communication.WriteAll(tmp, read, status, &error);
     if (error.Fail()) {
       return SendErrorResponse(0x15);
     }
+#endif
   }
 
   return SendOKResponse();
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 77506275d3e13..68f3eb0752973 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -101,6 +101,10 @@
 #include "llvm/Support/Threading.h"
 #include "llvm/Support/raw_ostream.h"
 
+#ifdef _WIN32
+#include "Plugins/Process/Windows/Common/IOHandlerProcessSTDIOWindows.h"
+#endif
+
 #if defined(__APPLE__)
 #define DEBUGSERVER_BASENAME "debugserver"
 #elif defined(_WIN32)
@@ -879,8 +883,18 @@ Status ProcessGDBRemote::DoLaunch(lldb_private::Module *exe_module,
       SetPrivateState(SetThreadStopInfo(response));
 
       if (!disable_stdio) {
-        if (pty.GetPrimaryFileDescriptor() != PseudoTerminal::invalid_fd)
+        if (pty.GetPrimaryFileDescriptor() != PseudoTerminal::invalid_fd) {
           SetSTDIOFileDescriptor(pty.ReleasePrimaryFileDescriptor());
+        }
+#ifdef _WIN32
+        else if (m_stdin_forward) {
+          // No client-side PTY FD on Windows.
+          std::lock_guard<std::mutex> guard(m_process_input_reader_mutex);
+          if (!m_process_input_reader)
+            m_process_input_reader =
+                std::make_shared<IOHandlerProcessSTDIOWindows>(this);
+        }
+#endif
       }
     }
   } else {

>From 0194f0dc723e7289bf744c1e3300e2699bedc931 Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Fri, 5 Jun 2026 17:38:51 +0100
Subject: [PATCH 2/2] fixup! [lldb][Windows] add stdin support to lldb-server

---
 .../source/Plugins/Process/Windows/Common/ProcessWindows.cpp | 1 -
 lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp  | 5 +----
 2 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
index 143f779d0b15b..30a1610ee5a17 100644
--- a/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
+++ b/lldb/source/Plugins/Process/Windows/Common/ProcessWindows.cpp
@@ -46,7 +46,6 @@
 #include "ExceptionRecord.h"
 #include "ForwardDecl.h"
 #include "LocalDebugDelegate.h"
-#include "Plugins/Process/Utility/IOHandlerProcessSTDIOWindows.h"
 #include "ProcessWindowsLog.h"
 #include "TargetThreadWindows.h"
 
diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
index 68f3eb0752973..16284f0052f5e 100644
--- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
+++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp
@@ -59,6 +59,7 @@
 #include "lldb/Target/ABI.h"
 #include "lldb/Target/DynamicLoader.h"
 #include "lldb/Target/MemoryRegionInfo.h"
+#include "lldb/Target/ProcessIOHandler.h"
 #include "lldb/Target/RegisterFlags.h"
 #include "lldb/Target/SystemRuntime.h"
 #include "lldb/Target/Target.h"
@@ -101,10 +102,6 @@
 #include "llvm/Support/Threading.h"
 #include "llvm/Support/raw_ostream.h"
 
-#ifdef _WIN32
-#include "Plugins/Process/Windows/Common/IOHandlerProcessSTDIOWindows.h"
-#endif
-
 #if defined(__APPLE__)
 #define DEBUGSERVER_BASENAME "debugserver"
 #elif defined(_WIN32)



More information about the lldb-commits mailing list