[lldb-dap][windows] add support for out of PATH python.dll resolution (#179306)

This commit is contained in:
Charles Zablit
2026-02-10 16:45:36 +01:00
committed by GitHub
parent a6929f7937
commit 67e571df8c
9 changed files with 185 additions and 97 deletions

View File

@@ -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")

View File

@@ -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

View File

@@ -65,6 +65,7 @@ add_host_subdirectory(posix
)
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
add_subdirectory(windows/PythonPathSetup)
add_host_subdirectory(windows
windows/ConnectionGenericFileWindows.cpp
windows/FileSystem.cpp

View File

@@ -0,0 +1,13 @@
add_lldb_library(lldbHostPythonPathSetup STATIC
PythonPathSetup.cpp
LINK_LIBS
LLVMSupport
)
if(DEFINED LLDB_PYTHON_DLL_RELATIVE_PATH)
target_compile_definitions(lldbHostPythonPathSetup PRIVATE LLDB_PYTHON_DLL_RELATIVE_PATH="${LLDB_PYTHON_DLL_RELATIVE_PATH}")
endif()
if(DEFINED LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME)
target_compile_definitions(lldbHostPythonPathSetup PRIVATE LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME="${LLDB_PYTHON_RUNTIME_LIBRARY_FILENAME}")
endif()

View File

@@ -0,0 +1,92 @@
//===----------------------------------------------------------------------===//
//
// 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(MAX_PATH);
while (buffer.size() <= PATHCCH_MAX_CCH) {
DWORD len = GetModuleFileNameW(NULL, buffer.data(), buffer.size());
if (len == 0)
return L"";
if (len < buffer.size())
return std::wstring(buffer.data(), len);
if (::GetLastError() == ERROR_INSUFFICIENT_BUFFER)
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();
}

View File

@@ -16,6 +16,16 @@ if (UNIX AND "${CMAKE_SYSTEM_NAME}" MATCHES "AIX")
add_definitions("-D_ALL_SOURCE")
endif()
set(LLDB_DRIVER_LINK_LIBS
liblldb
lldbHost
lldbUtility
)
if(WIN32)
list(APPEND LLDB_DRIVER_LINK_LIBS lldbHostPythonPathSetup)
endif()
add_lldb_tool(lldb
Driver.cpp
Platform.cpp
@@ -24,9 +34,7 @@ add_lldb_tool(lldb
Option
Support
LINK_LIBS
liblldb
lldbHost
lldbUtility
${LLDB_DRIVER_LINK_LIBS}
)
add_dependencies(lldb
@@ -34,13 +42,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.

View File

@@ -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) {
@@ -821,7 +737,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.

View File

@@ -2,11 +2,18 @@ set(LLVM_TARGET_DEFINITIONS Options.td)
tablegen(LLVM Options.inc -gen-opt-parser-defs)
add_public_tablegen_target(LLDBDAPOptionsTableGen)
set(LLDB_DAP_LINK_LIBS lldbDAP)
if(WIN32)
list(APPEND LLDB_DAP_LINK_LIBS lldbHostPythonPathSetup)
list(APPEND LLDB_DAP_LINK_LIBS liblldb)
endif()
add_lldb_tool(lldb-dap
lldb-dap.cpp
LINK_LIBS
lldbDAP
${LLDB_DAP_LINK_LIBS}
)
add_dependencies(lldb-dap

View File

@@ -70,6 +70,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>
@@ -522,6 +523,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;