LLDB mainline
PythonRuntimeLoader.cpp
Go to the documentation of this file.
1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "lldb/Host/Config.h"
11
14#include "lldb/Utility/Log.h"
15#include "llvm/ADT/SmallString.h"
16#include "llvm/Support/DynamicLibrary.h"
17#include "llvm/Support/ErrorExtras.h"
18#include "llvm/Support/Path.h"
19#include "llvm/Support/Threading.h"
20
21#include <cstdlib>
22#include <string>
23
24#if defined(_WIN32)
27#include "llvm/Support/Windows/WindowsSupport.h"
28#else
29#include <dlfcn.h>
30#endif
31
32namespace lldb_private {
33
34namespace {
35
36#if defined(_WIN32)
37#ifndef LOAD_LIBRARY_SEARCH_DEFAULT_DIRS
38#define LOAD_LIBRARY_SEARCH_DEFAULT_DIRS 0x00001000
39#endif
40
41using SetDefaultDllDirectoriesFn = BOOL(WINAPI *)(DWORD);
42using AddDllDirectoryFn = PVOID(WINAPI *)(PCWSTR);
43
44/// Forwarded exports resolve lazily, at an arbitrary later GetProcAddress
45/// call, so the directory can never be safely removed once added.
46void RegisterDllSearchDirectory(llvm::StringRef path) {
47 llvm::StringRef dir = llvm::sys::path::parent_path(path);
48 if (dir.empty())
49 return;
50
51 static LazyImport<AddDllDirectoryFn> add_dll_directory{L"Kernel32.dll",
52 "AddDllDirectory"};
53 if (!add_dll_directory)
54 return;
55
56 static llvm::once_flag opt_in_once;
57 llvm::call_once(opt_in_once, [] {
58 static LazyImport<SetDefaultDllDirectoriesFn> set_default_dll_directories{
59 L"Kernel32.dll", "SetDefaultDllDirectories"};
60 if (set_default_dll_directories)
61 (*set_default_dll_directories)(LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
62 });
63
64 llvm::SmallVector<wchar_t, MAX_PATH> wide_dir;
65 if (llvm::sys::windows::widenPath(dir, wide_dir))
66 return;
67 (*add_dll_directory)(wide_dir.data());
68}
69#endif // _WIN32
70
71/// True if libpython is currently mapped into the process. A single-symbol
72/// probe can match an incompatible runtime that happens to export it, so
73/// pair an old symbol with one introduced in 3.8 to bound the version on
74/// both ends.
75bool IsPythonAlreadyLoaded() {
76#if defined(_WIN32)
77 HMODULE main = ::GetModuleHandleW(nullptr);
78 return ::GetProcAddress(main, "Py_IsInitialized") != nullptr &&
79 ::GetProcAddress(main, "Py_InitializeFromConfig") != nullptr;
80#else
81 return ::dlsym(RTLD_DEFAULT, "Py_IsInitialized") != nullptr &&
82 ::dlsym(RTLD_DEFAULT, "Py_InitializeFromConfig") != nullptr;
83#endif
84}
85
86llvm::Error TryLoad(const char *path) {
87#if defined(_WIN32)
88 RegisterDllSearchDirectory(path);
89#endif
90 std::string err_msg;
91 llvm::sys::DynamicLibrary lib =
92 llvm::sys::DynamicLibrary::getPermanentLibrary(path, &err_msg);
93 if (!lib.isValid())
94 return llvm::createStringErrorV("could not load '{0}': {1}", path, err_msg);
95
96 // A successful load does not imply the library is actually libpython,
97 // so reject anything that doesn't expose a stable-ABI symbol.
98 if (!lib.getAddressOfSymbol("Py_IsInitialized"))
99 return llvm::createStringErrorV(
100 "'{0}' does not export Py_IsInitialized; not a Python runtime", path);
101
102 return llvm::Error::success();
103}
104
105llvm::Expected<std::string> LoadPythonRuntime() {
106 if (IsPythonAlreadyLoaded())
107 return std::string();
108
109 const char *override_path = std::getenv("LLDB_PYTHON_LIBRARY");
110 if (override_path && !*override_path)
111 override_path = nullptr;
112
113 llvm::Error failures = llvm::createStringError(
114 override_path ? "could not locate the Python runtime library"
115 : "could not locate the Python runtime library, set "
116 "LLDB_PYTHON_LIBRARY to its path");
117 std::string loaded;
118
119 auto try_path = [&](const char *path) -> bool {
120 if (llvm::Error err = TryLoad(path)) {
121 failures = llvm::joinErrors(std::move(failures), std::move(err));
122 return false;
123 }
124 loaded = path;
125 return true;
126 };
127
128 if (override_path && try_path(override_path)) {
129 consumeError(std::move(failures));
130 return loaded;
131 }
132
133#ifdef LLDB_PYTHON_RUNTIME_LIBRARY_BUILD_PATH
134 // The build-time Python is the closest stable-ABI match for what the
135 // plugin was compiled against, so prefer it over the platform fallbacks.
136 if (try_path(LLDB_PYTHON_RUNTIME_LIBRARY_BUILD_PATH)) {
137 consumeError(std::move(failures));
138 return loaded;
139 }
140#endif
141
142 bool found = false;
144 [&](const char *candidate) { return found = try_path(candidate); });
145
146 if (found) {
147 consumeError(std::move(failures));
148 return loaded;
149 }
150 return std::move(failures);
151}
152
153class PythonRuntimeLoader : public ScriptInterpreterRuntimeLoader {
154public:
155 llvm::Error Load() override {
156 EnsureLoaded();
157 if (m_error_message.empty())
158 return llvm::Error::success();
159 return llvm::createStringError(m_error_message);
160 }
161
162 llvm::Expected<llvm::StringRef> GetLoadedPath() override {
163 EnsureLoaded();
164 if (!m_error_message.empty())
165 return llvm::createStringError(m_error_message);
166 return llvm::StringRef(m_path);
167 }
168
169 bool IsLoaded() override { return IsPythonAlreadyLoaded(); }
170
171private:
172 void EnsureLoaded() {
173 llvm::call_once(m_once, [this] {
174 if (llvm::Expected<std::string> result = LoadPythonRuntime()) {
175 m_path = std::move(*result);
176 if (m_path.empty())
177 return;
178#if defined(_WIN32)
179 // liblldb.dll may link against (lib)python3.dll (stable ABI). Ensure
180 // it is loaded from the same directory as the version-specific runtime
181 // so the delay-load resolver can find it.
182 llvm::SmallString<256> stable_abi_path(m_path);
183 llvm::sys::path::remove_filename(stable_abi_path);
184#if defined(__MINGW32__)
185 llvm::sys::path::append(stable_abi_path, "libpython3.dll");
186#else
187 llvm::sys::path::append(stable_abi_path, "python3.dll");
188#endif
189 std::string err;
190 llvm::sys::DynamicLibrary::getPermanentLibrary(stable_abi_path.c_str(),
191 &err);
192#endif
193 } else {
194 m_error_message = llvm::toString(result.takeError());
195 LLDB_LOG(GetLog(LLDBLog::Host), "Python runtime load failed: {0}",
196 m_error_message);
197 }
198 });
199 }
200
201 llvm::once_flag m_once;
202 std::string m_path;
203 std::string m_error_message;
204};
205
206} // namespace
207
208llvm::Expected<ScriptInterpreterRuntimeLoader &>
210 switch (language) {
212 static PythonRuntimeLoader instance;
213 return instance;
214 }
218 return llvm::createStringError(
219 "no runtime loader for the requested script language");
220 }
221 llvm_unreachable("unhandled ScriptLanguage");
222}
223
224} // namespace lldb_private
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:375
Loads a script-interpreter runtime into the current process before its plugin is dlopened.
static llvm::Expected< ScriptInterpreterRuntimeLoader & > Get(lldb::ScriptLanguage language)
Returns the loader for language.
A class that represents a running process on the host machine.
Log * GetLog(Cat mask)
Retrieve the Log object for the channel associated with the given log enum.
Definition Log.h:338
void ForEachPythonRuntimeCandidate(llvm::function_ref< bool(const char *)> callback)
Visits candidate Python runtime paths in priority order, stopping at the first call that returns true...
static std::enable_if_t< is_load< I >, bool > Load(EmulateInstructionRISCV &emulator, I inst, uint64_t(*extend)(E))
ScriptLanguage
Script interpreter types.
@ eScriptLanguageUnknown
@ eScriptLanguageNone
@ eScriptLanguagePython