[Lldb-commits] [lldb] [lldb-dap][windows] add support for out of PATH python.dll resolution (PR #179306)
Charles Zablit via lldb-commits
lldb-commits at lists.llvm.org
Tue Feb 3 09:08:28 PST 2026
https://github.com/charles-zablit updated https://github.com/llvm/llvm-project/pull/179306
>From 54fc4ef2f3307632648a61794ad3def9fc4d1c1e Mon Sep 17 00:00:00 2001
From: Charles Zablit <c_zablit at apple.com>
Date: Tue, 3 Feb 2026 17:08:15 +0000
Subject: [PATCH] [lldb-dap][windows] add support for out of PATH python.dll
resolution
---
lldb/CMakeLists.txt | 4 +
.../windows/PythonPathSetup/PythonPathSetup.h | 47 ++++++++++
lldb/source/Host/CMakeLists.txt | 2 +
.../windows/PythonPathSetup/CMakeLists.txt | 24 +++++
.../PythonPathSetup/PythonPathSetup.cpp | 89 +++++++++++++++++++
lldb/tools/driver/CMakeLists.txt | 8 +-
lldb/tools/driver/Driver.cpp | 89 +------------------
lldb/tools/lldb-dap/tool/CMakeLists.txt | 2 +
lldb/tools/lldb-dap/tool/lldb-dap.cpp | 6 ++
9 files changed, 178 insertions(+), 93 deletions(-)
create mode 100644 lldb/include/lldb/Host/windows/PythonPathSetup/PythonPathSetup.h
create mode 100644 lldb/source/Host/windows/PythonPathSetup/CMakeLists.txt
create mode 100644 lldb/source/Host/windows/PythonPathSetup/PythonPathSetup.cpp
diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt
index 0736e6ba132c8..0a1ca5cf391ef 100644
--- a/lldb/CMakeLists.txt
+++ b/lldb/CMakeLists.txt
@@ -61,6 +61,10 @@ if (LLDB_ENABLE_PYTHON)
"Path to python interpreter exectuable, relative to python's install prefix")
set(cachestring_LLDB_PYTHON_EXT_SUFFIX
"Filename extension for native code python modules")
+ set(cachestring_LLDB_PYTHON_DLL_RELATIVE_PATH
+ "Relative path from LLDB executable to Python DLL directory (Windows only)")
+ set(cachestring_LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME
+ "Filename of Python runtime library to search for, e.g. python311.dll (Windows only)")
if (LLDB_ENABLE_PYTHON_LIMITED_API)
set(stable_abi "--stable-abi")
diff --git a/lldb/include/lldb/Host/windows/PythonPathSetup/PythonPathSetup.h b/lldb/include/lldb/Host/windows/PythonPathSetup/PythonPathSetup.h
new file mode 100644
index 0000000000000..5016e35304f70
--- /dev/null
+++ b/lldb/include/lldb/Host/windows/PythonPathSetup/PythonPathSetup.h
@@ -0,0 +1,47 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_HOST_PYTHONPATHSETUP_H
+#define LLDB_SOURCE_HOST_PYTHONPATHSETUP_H
+
+#include "llvm/Support/Error.h"
+
+#ifdef LLDB_PYTHON_DLL_RELATIVE_PATH
+/// Resolve the full path of the directory defined by
+/// LLDB_PYTHON_DLL_RELATIVE_PATH. If it exists, add it to the list of DLL
+/// search directories.
+///
+/// \return `true` if the library was added to the search path.
+/// `false` otherwise.
+bool AddPythonDLLToSearchPath();
+#endif
+
+/// Attempts to setup the DLL search path for the Python runtime library.
+///
+/// In the following paragraphs, python3xx.dll refers to the Python runtime
+/// library name which is defined by LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME, e.g.
+/// python311.dll for Python 3.11.
+///
+/// The setup flow depends on which macros are defined:
+///
+/// - If only LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME is defined, checks whether
+/// python3xx.dll can be loaded. Returns an error if it cannot.
+///
+/// - If only LLDB_PYTHON_DLL_RELATIVE_PATH is defined, attempts to resolve the
+/// relative path and add it to the DLL search path. Returns an error if this
+/// fails. Note that this may succeed even if python3xx.dll is not present in
+/// the added search path.
+///
+/// - If both LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME and
+/// LLDB_PYTHON_DLL_RELATIVE_PATH are defined, first checks if python3xx.dll
+/// can be loaded. If successful, returns immediately. Otherwise, attempts to
+/// resolve the relative path and add it to the DLL search path, then checks
+/// again if python3xx.dll can be loaded.
+llvm::Error SetupPythonRuntimeLibrary();
+
+#endif // LLDB_SOURCE_HOST_PYTHONPATHSETUP_H
diff --git a/lldb/source/Host/CMakeLists.txt b/lldb/source/Host/CMakeLists.txt
index 8ad485fa40285..8c2e44cf565cc 100644
--- a/lldb/source/Host/CMakeLists.txt
+++ b/lldb/source/Host/CMakeLists.txt
@@ -1,3 +1,5 @@
+add_subdirectory(PythonPathSetup)
+
if (APPLE AND LLVM_ENABLE_LOCAL_SUBMODULE_VISIBILITY)
# The arpa/inet.h header used in the files here is providing a miscompiled
# htonl function on macOS < 14 when local submodule visibility is active.
diff --git a/lldb/source/Host/windows/PythonPathSetup/CMakeLists.txt b/lldb/source/Host/windows/PythonPathSetup/CMakeLists.txt
new file mode 100644
index 0000000000000..a5df219759762
--- /dev/null
+++ b/lldb/source/Host/windows/PythonPathSetup/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Configure Python runtime library path for Windows
+#
+# LLDB_PYTHON_DLL_RELATIVE_PATH:
+# Relative path from the LLDB executable to the directory containing the
+# Python runtime DLL. Used on Windows to locate python3X.dll at runtime.
+# Example: "../../../tools/python3/DLLs"
+#
+# LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME:
+# The filename of the Python runtime library to search for (e.g., "python310.dll").
+# Used on Windows to verify the correct Python DLL is found in the search path.
+
+add_lldb_library(lldbUtilityPythonPathSetup STATIC
+ PythonPathSetup.cpp
+
+ LINK_LIBS
+ LLVMSupport
+)
+
+if(DEFINED LLDB_PYTHON_DLL_RELATIVE_PATH)
+ target_compile_definitions(lldbUtilityPythonPathSetup PRIVATE LLDB_PYTHON_DLL_RELATIVE_PATH="${LLDB_PYTHON_DLL_RELATIVE_PATH}")
+endif()
+if(DEFINED LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME)
+ target_compile_definitions(lldbUtilityPythonPathSetup PRIVATE LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME="${LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME}")
+endif()
diff --git a/lldb/source/Host/windows/PythonPathSetup/PythonPathSetup.cpp b/lldb/source/Host/windows/PythonPathSetup/PythonPathSetup.cpp
new file mode 100644
index 0000000000000..1d6cbff79fd0b
--- /dev/null
+++ b/lldb/source/Host/windows/PythonPathSetup/PythonPathSetup.cpp
@@ -0,0 +1,89 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "lldb/Host/windows/PythonPathSetup/PythonPathSetup.h"
+
+#include "lldb/Host/windows/windows.h"
+#include "llvm/Support/Windows/WindowsSupport.h"
+
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/ConvertUTF.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include <pathcch.h>
+
+using namespace llvm;
+
+#ifdef LLDB_PYTHON_DLL_RELATIVE_PATH
+/// Returns the full path to the lldb.exe executable.
+static std::wstring GetPathToExecutableW() {
+ std::vector<WCHAR> buffer;
+ buffer.resize(MAX_PATH /*=260*/);
+ while (buffer.size() < PATHCCH_MAX_CCH /*=32,767*/) {
+ if (GetModuleFileNameW(NULL, buffer.data(), buffer.size()) < buffer.size())
+ return std::wstring(buffer.begin(), buffer.end());
+ buffer.resize(buffer.size() * 2);
+ }
+ return L"";
+}
+
+bool AddPythonDLLToSearchPath() {
+ std::wstring modulePath = GetPathToExecutableW();
+ if (modulePath.empty())
+ return false;
+
+ SmallVector<char, MAX_PATH> utf8Path;
+ if (sys::windows::UTF16ToUTF8(modulePath.c_str(), modulePath.length(),
+ utf8Path))
+ return false;
+ sys::path::remove_filename(utf8Path);
+ sys::path::append(utf8Path, LLDB_PYTHON_DLL_RELATIVE_PATH);
+ sys::fs::make_absolute(utf8Path);
+
+ SmallVector<wchar_t, 1> widePath;
+ if (sys::windows::widenPath(utf8Path.data(), widePath))
+ return false;
+
+ if (sys::fs::exists(utf8Path))
+ return SetDllDirectoryW(widePath.data());
+ return false;
+}
+#endif
+
+#ifdef LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME
+bool IsPythonDLLInPath() {
+#define WIDEN2(x) L##x
+#define WIDEN(x) WIDEN2(x)
+ HMODULE h = LoadLibraryW(WIDEN(LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME));
+ if (!h)
+ return false;
+ FreeLibrary(h);
+ return true;
+#undef WIDEN2
+#undef WIDEN
+}
+#endif
+
+llvm::Error SetupPythonRuntimeLibrary() {
+#ifdef LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME
+ if (IsPythonDLLInPath())
+ return Error::success();
+#ifdef LLDB_PYTHON_DLL_RELATIVE_PATH
+ if (AddPythonDLLToSearchPath() && IsPythonDLLInPath())
+ return Error::success();
+#endif
+ return createStringError(
+ inconvertibleErrorCode(),
+ "unable to find '" LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME "'");
+#elif defined(LLDB_PYTHON_DLL_RELATIVE_PATH)
+ if (!AddPythonDLLToSearchPath())
+ return createStringError(inconvertibleErrorCode(),
+ "unable to find the Python runtime library");
+#endif
+ return Error::success();
+}
diff --git a/lldb/tools/driver/CMakeLists.txt b/lldb/tools/driver/CMakeLists.txt
index efe51506f3545..c0973b81368d8 100644
--- a/lldb/tools/driver/CMakeLists.txt
+++ b/lldb/tools/driver/CMakeLists.txt
@@ -24,6 +24,7 @@ add_lldb_tool(lldb
Option
Support
LINK_LIBS
+ lldbUtilityPythonPathSetup
liblldb
lldbHost
lldbUtility
@@ -34,13 +35,6 @@ add_dependencies(lldb
${tablegen_deps}
)
-if(DEFINED LLDB_PYTHON_DLL_RELATIVE_PATH)
- target_compile_definitions(lldb PRIVATE LLDB_PYTHON_DLL_RELATIVE_PATH="${LLDB_PYTHON_DLL_RELATIVE_PATH}")
-endif()
-if(DEFINED LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME)
- target_compile_definitions(lldb PRIVATE LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME="${LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME}")
-endif()
-
if(LLDB_BUILD_FRAMEWORK)
# In the build-tree, we know the exact path to the framework directory.
# The installed framework can be in different locations.
diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp
index 48107717abd31..b2b1461120a08 100644
--- a/lldb/tools/driver/Driver.cpp
+++ b/lldb/tools/driver/Driver.cpp
@@ -34,7 +34,7 @@
#include "llvm/Support/raw_ostream.h"
#ifdef _WIN32
-#include "llvm/Support/Windows/WindowsSupport.h"
+#include "lldb/Host/windows/PythonPathSetup/PythonPathSetup.h"
#endif
#include <algorithm>
@@ -433,90 +433,6 @@ SBError Driver::ProcessArgs(const opt::InputArgList &args, bool &exiting) {
return error;
}
-#ifdef _WIN32
-#ifdef LLDB_PYTHON_DLL_RELATIVE_PATH
-/// Returns the full path to the lldb.exe executable.
-inline std::wstring GetPathToExecutableW() {
- // Iterate until we reach the Windows API maximum path length (32,767).
- std::vector<WCHAR> buffer;
- buffer.resize(MAX_PATH /*=260*/);
- while (buffer.size() < 32767) {
- if (GetModuleFileNameW(NULL, buffer.data(), buffer.size()) < buffer.size())
- return std::wstring(buffer.begin(), buffer.end());
- buffer.resize(buffer.size() * 2);
- }
- return L"";
-}
-
-/// \brief Resolve the full path of the directory defined by
-/// LLDB_PYTHON_DLL_RELATIVE_PATH. If it exists, add it to the list of DLL
-/// search directories.
-/// \return `true` if the library was added to the search path.
-/// `false` otherwise.
-bool AddPythonDLLToSearchPath() {
- std::wstring modulePath = GetPathToExecutableW();
- if (modulePath.empty())
- return false;
-
- SmallVector<char, MAX_PATH> utf8Path;
- if (sys::windows::UTF16ToUTF8(modulePath.c_str(), modulePath.length(),
- utf8Path))
- return false;
- sys::path::remove_filename(utf8Path);
- sys::path::append(utf8Path, LLDB_PYTHON_DLL_RELATIVE_PATH);
- sys::fs::make_absolute(utf8Path);
-
- SmallVector<wchar_t, 1> widePath;
- if (sys::windows::widenPath(utf8Path.data(), widePath))
- return false;
-
- if (sys::fs::exists(utf8Path))
- return SetDllDirectoryW(widePath.data());
- return false;
-}
-#endif
-
-#ifdef LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME
-/// Returns true if `python3x.dll` can be loaded.
-bool IsPythonDLLInPath() {
-#define WIDEN2(x) L##x
-#define WIDEN(x) WIDEN2(x)
- HMODULE h = LoadLibraryW(WIDEN(LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME));
- if (!h)
- return false;
- FreeLibrary(h);
- return true;
-#undef WIDEN2
-#undef WIDEN
-}
-#endif
-
-/// Try to setup the DLL search path for the Python Runtime Library
-/// (python3xx.dll).
-///
-/// If `LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME` is set, we first check if
-/// python3xx.dll is in the search path. If it's not, we try to add it and
-/// check for it a second time.
-/// If only `LLDB_PYTHON_DLL_RELATIVE_PATH` is set, we try to add python3xx.dll
-/// to the search path python.dll is already in the search path or not.
-void SetupPythonRuntimeLibrary() {
-#ifdef LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME
- if (IsPythonDLLInPath())
- return;
-#ifdef LLDB_PYTHON_DLL_RELATIVE_PATH
- if (AddPythonDLLToSearchPath() && IsPythonDLLInPath())
- return;
-#endif
- WithColor::error() << "unable to find '"
- << LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME << "'.\n";
- return;
-#elif defined(LLDB_PYTHON_DLL_RELATIVE_PATH)
- if (!AddPythonDLLToSearchPath())
- WithColor::error() << "unable to find the Python runtime library.\n";
-#endif
-}
-#endif
-
std::string EscapeString(std::string arg) {
std::string::size_type pos = 0;
while ((pos = arg.find_first_of("\"\\", pos)) != std::string::npos) {
@@ -820,7 +736,8 @@ int main(int argc, char const *argv[]) {
#endif
#ifdef _WIN32
- SetupPythonRuntimeLibrary();
+ if (llvm::Error error = SetupPythonRuntimeLibrary())
+ llvm::WithColor::error() << llvm::toString(std::move(error)) << '\n';
#endif
// Parse arguments.
diff --git a/lldb/tools/lldb-dap/tool/CMakeLists.txt b/lldb/tools/lldb-dap/tool/CMakeLists.txt
index 692aeedd9531b..d75ac02b67708 100644
--- a/lldb/tools/lldb-dap/tool/CMakeLists.txt
+++ b/lldb/tools/lldb-dap/tool/CMakeLists.txt
@@ -6,6 +6,8 @@ add_lldb_tool(lldb-dap
lldb-dap.cpp
LINK_LIBS
+ lldbUtilityPythonPathSetup
+ liblldb # delay load liblldb
lldbDAP
)
diff --git a/lldb/tools/lldb-dap/tool/lldb-dap.cpp b/lldb/tools/lldb-dap/tool/lldb-dap.cpp
index 15c63543e86f5..795802c38adea 100644
--- a/lldb/tools/lldb-dap/tool/lldb-dap.cpp
+++ b/lldb/tools/lldb-dap/tool/lldb-dap.cpp
@@ -71,6 +71,7 @@
#undef GetObject
#include <io.h>
typedef int socklen_t;
+#include "lldb/Host/windows/PythonPathSetup/PythonPathSetup.h"
#else
#include <netinet/in.h>
#include <sys/socket.h>
@@ -523,6 +524,11 @@ int main(int argc, char *argv[]) {
"~/Library/Logs/DiagnosticReports/.\n");
#endif
+#ifdef _WIN32
+ if (llvm::Error error = SetupPythonRuntimeLibrary())
+ llvm::WithColor::error() << llvm::toString(std::move(error)) << '\n';
+#endif
+
llvm::SmallString<256> program_path(argv[0]);
llvm::sys::fs::make_absolute(program_path);
DAP::debug_adapter_path = program_path;
More information about the lldb-commits
mailing list