https://github.com/llvmbot created https://github.com/llvm/llvm-project/pull/223004
Backport 1ca6698be7708121f987d0acf896c7dc4cf63a51 ba1687b72476eb368641b9a9f565a38cf41b7393 Requested by: @Nerixyz >From 5ceea691d6009231a06f54544bfe396f5c8865cc Mon Sep 17 00:00:00 2001 From: Nerixyz <[email protected]> Date: Tue, 8 Sep 2026 19:35:44 +0200 Subject: [PATCH 1/2] [lldb][Windows] Translate file descriptors between CRT instances (#217126) The official LLVM and lldb builds use `LLVM_ENABLE_RPMALLOC`. This changes the default CRT allocator to rpmalloc for performance reasons. In doing so, it also changes the CRT linkage from dynamic (`/MD`) to static (`/MT`). Since the CRT is the one managing file descriptors on Windows, its universe is now scoped to a module. For lldb, this means we have three fd-spaces: the driver (lldb/lldb-dap), liblldb, and Python. We usually pass files around as file descriptors. This breaks when we do it between modules. Fortunately, the C runtime provides us with helpers to translate between the process scoped HANDLEs and file descriptors. This way, we can allocate an fd for a handle passed across modules. For Python, there's the [`msvcrt`](https://docs.python.org/3/library/msvcrt.html) library we can use to translate. Fixes #216679. (cherry picked from commit 1ca6698be7708121f987d0acf896c7dc4cf63a51) --- lldb/include/lldb/API/SBFile.h | 4 + lldb/source/API/SBFile.cpp | 6 ++ .../Python/PythonDataObjects.cpp | 77 ++++++++++++++++++- .../Python/PythonDataObjects.h | 3 + .../Python/ScriptInterpreterPython.cpp | 6 +- lldb/tools/lldb-dap/DAP.cpp | 15 +++- 6 files changed, 105 insertions(+), 6 deletions(-) diff --git a/lldb/include/lldb/API/SBFile.h b/lldb/include/lldb/API/SBFile.h index 8cf4fe1b405fa..835c70f480595 100644 --- a/lldb/include/lldb/API/SBFile.h +++ b/lldb/include/lldb/API/SBFile.h @@ -35,6 +35,10 @@ class LLDB_API SBFile { SBFile(int fd, const char *mode, bool transfer_ownership); ~SBFile(); +#if defined(_WIN32) && !defined(SWIG) + static int OpenFdFromHandle(intptr_t handle, int flags); +#endif + SBFile &operator=(const SBFile &rhs); SBError Read(uint8_t *buf, size_t num_bytes, size_t *OUTPUT); diff --git a/lldb/source/API/SBFile.cpp b/lldb/source/API/SBFile.cpp index a93f25389d2e0..c2f0ff89367b5 100644 --- a/lldb/source/API/SBFile.cpp +++ b/lldb/source/API/SBFile.cpp @@ -69,6 +69,12 @@ SBFile::SBFile(int fd, const char *mode, bool transfer_ownership) { std::make_shared<NativeFile>(fd, options.get(), transfer_ownership); } +#ifdef _WIN32 +int SBFile::OpenFdFromHandle(intptr_t handle, int flags) { + return _open_osfhandle(handle, flags); +} +#endif + SBError SBFile::Read(uint8_t *buf, size_t num_bytes, size_t *bytes_read) { LLDB_INSTRUMENT_VA(this, buf, num_bytes, bytes_read); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp index fba98abf9e83b..7c9dbbd4852c8 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -21,6 +21,10 @@ #include "llvm/Support/ConvertUTF.h" #include "llvm/Support/Errno.h" +#ifdef _WIN32 +#include "lldb/Host/windows/windows.h" +#endif + #include <cstdio> #include <variant> @@ -906,6 +910,63 @@ bool PythonFile::Check(PyObject *py_obj) { return !!r; } +#if defined(_WIN32) && !defined(_DLL) +// When LLVM is built with a different CRT allocator, it's built against the +// static C runtime. The official Python builds link to the dynamic C runtime. +// Since the file descriptors are managed per CRT instance, liblldb and Python +// have different fd mappings. This translates between the two using the msvcrt +// module. +int PythonFile::TranslateFdToPython(int our_fd) { + intptr_t handle = _get_osfhandle(our_fd); + if (handle == 0 || (HANDLE)handle == INVALID_HANDLE_VALUE) + return -1; + + PyObject *msvcrt = PyImport_ImportModule("msvcrt"); + if (!msvcrt) + return -1; + PyObject *open_osf = PyObject_GetAttrString(msvcrt, "open_osfhandle"); + Py_XDECREF(msvcrt); + if (!open_osf) + return -1; + PyObject *fd_obj = + PyObject_CallFunction(open_osf, "Li", (long long)handle, 0); + Py_XDECREF(open_osf); + if (!fd_obj) + return -1; + if (!PyLong_Check(fd_obj)) { + Py_XDECREF(fd_obj); + return -1; + } + long theirs = PyLong_AsLong(fd_obj); + Py_XDECREF(fd_obj); + return (int)theirs; +} + +int PythonFile::TranslateFdFromPython(int their_fd) { + PyObject *msvcrt = PyImport_ImportModule("msvcrt"); + if (!msvcrt) + return -1; + PyObject *get_handle = PyObject_GetAttrString(msvcrt, "get_osfhandle"); + Py_XDECREF(msvcrt); + if (!get_handle) + return -1; + PyObject *handle_obj = PyObject_CallFunction(get_handle, "i", their_fd); + Py_XDECREF(get_handle); + if (!handle_obj) + return -1; + if (!PyLong_Check(handle_obj)) { + Py_XDECREF(handle_obj); + return -1; + } + size_t handle = PyLong_AsSize_t(handle_obj); + Py_XDECREF(handle_obj); + return _open_osfhandle((intptr_t)handle, 0); +} +#else +int PythonFile::TranslateFdToPython(int our_fd) { return our_fd; } +int PythonFile::TranslateFdFromPython(int their_fd) { return their_fd; } +#endif + const char *PythonException::toCString() const { if (!m_repr_bytes) return "unknown exception"; @@ -1274,6 +1335,12 @@ llvm::Expected<FileSP> PythonFile::ConvertToFile(bool borrowed) { PyErr_Clear(); return ConvertToFileForcingUseOfScriptingIOMethods(borrowed); } + fd = TranslateFdFromPython(fd); + if (fd < 0) { + PyErr_Clear(); + return llvm::createStringError("failed to translate Python fd to our fd"); + } + auto options = GetOptionsForPyObject(*this); if (!options) return options.takeError(); @@ -1318,6 +1385,12 @@ PythonFile::ConvertToFileForcingUseOfScriptingIOMethods(bool borrowed) { if (fd < 0) { PyErr_Clear(); fd = File::kInvalidDescriptor; + } else { + fd = TranslateFdFromPython(fd); + if (fd < 0) { + PyErr_Clear(); + return llvm::createStringError("failed to translate Python fd to our fd"); + } } auto io_module = PythonModule::Import("io"); @@ -1383,8 +1456,8 @@ Expected<PythonFile> PythonFile::FromFile(File &file, const char *mode) { } PyObject *file_obj; - file_obj = PyFile_FromFd(file.GetDescriptor(), nullptr, mode, -1, nullptr, - "ignore", nullptr, /*closefd=*/0); + file_obj = PyFile_FromFd(TranslateFdToPython(file.GetDescriptor()), nullptr, + mode, -1, nullptr, "ignore", nullptr, /*closefd=*/0); if (!file_obj) return exception(); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h index 3f2b869bcfb0a..be97d1c7e0ca7 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.h @@ -635,6 +635,9 @@ class PythonFile : public TypedPythonObject<PythonFile> { static bool Check(PyObject *py_obj); + static int TranslateFdToPython(int our_fd); + static int TranslateFdFromPython(int their_fd); + static llvm::Expected<PythonFile> FromFile(File &file, const char *mode = nullptr); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp index 7b1bd9d8411c5..6316787fdefc7 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp @@ -1088,8 +1088,10 @@ bool ScriptInterpreterPythonImpl::RedirectTerminalHandleThroughLock( // terminal) promptly: the pipe descriptor is not a tty, so the default // buffering would hold output back until the buffer filled. PyObject *pipe_file = PyFile_FromFd( - redirect->GetWriteDescriptor(), nullptr, mode, /*buffering=*/1, - /*encoding=*/nullptr, /*errors=*/"ignore", /*newline=*/nullptr, + PythonFile::TranslateFdToPython(redirect->GetWriteDescriptor()), nullptr, + mode, /*buffering=*/1, + /*encoding=*/nullptr, /*errors=*/"ignore", + /*newline=*/nullptr, /*closefd=*/0); if (!pipe_file) { // Fall back to the raw descriptor. That reopens the statusline race, so diff --git a/lldb/tools/lldb-dap/DAP.cpp b/lldb/tools/lldb-dap/DAP.cpp index efd10698b9fb8..81492ebc1ed9c 100644 --- a/lldb/tools/lldb-dap/DAP.cpp +++ b/lldb/tools/lldb-dap/DAP.cpp @@ -1319,11 +1319,22 @@ llvm::Error DAP::InitializeDebugger() { llvm::Expected<int> out_fd = out.GetWriteFileDescriptor(); if (!out_fd) return out_fd.takeError(); - debugger.SetOutputFile(lldb::SBFile(*out_fd, "w", false)); - llvm::Expected<int> err_fd = err.GetWriteFileDescriptor(); if (!err_fd) return err_fd.takeError(); + +#if defined(_WIN32) && !defined(_DLL) + // When LLVM is built with a different CRT allocator, it's built against the + // static C runtime. Since the C runtime is the one managing the file + // descriptors, its state is now local to each module. Here, lldb-dap and + // liblldb have two different CRT states and thus different sets of file + // descriptors. This translates an lldb-dap fd into a liblldb fd by creating a + // mapping of fd -> HANDLE inside liblldb. + *out_fd = lldb::SBFile::OpenFdFromHandle(_get_osfhandle(*out_fd), 0); + *err_fd = lldb::SBFile::OpenFdFromHandle(_get_osfhandle(*err_fd), 0); +#endif + + debugger.SetOutputFile(lldb::SBFile(*out_fd, "w", false)); debugger.SetErrorFile(lldb::SBFile(*err_fd, "w", false)); // The sourceInitFile option is not part of the DAP specification. It is an >From c84f3f8d997b6237e11bab6f82d93de61eba68b5 Mon Sep 17 00:00:00 2001 From: Nerixyz <[email protected]> Date: Fri, 11 Sep 2026 19:50:45 +0200 Subject: [PATCH 2/2] [lldb][Windows] Remove use of unstable ABI and fix SB API visibility (#222665) Addresses the post-commit review comments from the original PR (https://github.com/llvm/llvm-project/pull/217126#pullrequestreview-5162589053). - `SBFile::OpenFdFromHandle` is now visible on all platforms (just `#ifndef SWIG`) and returns -1 by default. - Removed non-stable `PyLong_Check` in favor of `PyErr_Occurred` and `PyErr_Clear`. (cherry picked from commit ba1687b72476eb368641b9a9f565a38cf41b7393) --- lldb/include/lldb/API/SBFile.h | 8 +++++++- lldb/source/API/SBFile.cpp | 8 ++++++-- .../Python/PythonDataObjects.cpp | 19 +++++++++++-------- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lldb/include/lldb/API/SBFile.h b/lldb/include/lldb/API/SBFile.h index 835c70f480595..73b7dc461b443 100644 --- a/lldb/include/lldb/API/SBFile.h +++ b/lldb/include/lldb/API/SBFile.h @@ -35,7 +35,13 @@ class LLDB_API SBFile { SBFile(int fd, const char *mode, bool transfer_ownership); ~SBFile(); -#if defined(_WIN32) && !defined(SWIG) +#ifndef SWIG + /// Open a file descriptor in liblldb from a Windows HANDLE. + /// + /// This is useful for builds that statically link to the C runtime (`/MT`), + /// because the fd -> HANDLE mapping is local to liblldb's CRT instance. + /// + /// On other platforms, this always returns -1. static int OpenFdFromHandle(intptr_t handle, int flags); #endif diff --git a/lldb/source/API/SBFile.cpp b/lldb/source/API/SBFile.cpp index c2f0ff89367b5..ab8e6e152d39e 100644 --- a/lldb/source/API/SBFile.cpp +++ b/lldb/source/API/SBFile.cpp @@ -69,11 +69,15 @@ SBFile::SBFile(int fd, const char *mode, bool transfer_ownership) { std::make_shared<NativeFile>(fd, options.get(), transfer_ownership); } -#ifdef _WIN32 int SBFile::OpenFdFromHandle(intptr_t handle, int flags) { +#if _WIN32 return _open_osfhandle(handle, flags); -} +#else + (void)handle; + (void)flags; + return -1; #endif +} SBError SBFile::Read(uint8_t *buf, size_t num_bytes, size_t *bytes_read) { LLDB_INSTRUMENT_VA(this, buf, num_bytes, bytes_read); diff --git a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp index 7c9dbbd4852c8..8b2d66a168c73 100644 --- a/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp +++ b/lldb/source/Plugins/ScriptInterpreter/Python/PythonDataObjects.cpp @@ -933,12 +933,13 @@ int PythonFile::TranslateFdToPython(int our_fd) { Py_XDECREF(open_osf); if (!fd_obj) return -1; - if (!PyLong_Check(fd_obj)) { - Py_XDECREF(fd_obj); - return -1; - } + long theirs = PyLong_AsLong(fd_obj); Py_XDECREF(fd_obj); + if (PyErr_Occurred()) { + PyErr_Clear(); + return -1; + } return (int)theirs; } @@ -954,12 +955,14 @@ int PythonFile::TranslateFdFromPython(int their_fd) { Py_XDECREF(get_handle); if (!handle_obj) return -1; - if (!PyLong_Check(handle_obj)) { - Py_XDECREF(handle_obj); - return -1; - } + size_t handle = PyLong_AsSize_t(handle_obj); Py_XDECREF(handle_obj); + if (PyErr_Occurred()) { + PyErr_Clear(); + return -1; + } + return _open_osfhandle((intptr_t)handle, 0); } #else _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
