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)
26#else
27#include <dlfcn.h>
28#endif
29
30namespace lldb_private {
31
32namespace {
33
34/// True if libpython is currently mapped into the process. A single-symbol
35/// probe can match an incompatible runtime that happens to export it, so
36/// pair an old symbol with one introduced in 3.8 to bound the version on
37/// both ends.
38bool IsPythonAlreadyLoaded() {
39#if defined(_WIN32)
40 HMODULE main = ::GetModuleHandleW(nullptr);
41 return ::GetProcAddress(main, "Py_IsInitialized") != nullptr &&
42 ::GetProcAddress(main, "Py_InitializeFromConfig") != nullptr;
43#else
44 return ::dlsym(RTLD_DEFAULT, "Py_IsInitialized") != nullptr &&
45 ::dlsym(RTLD_DEFAULT, "Py_InitializeFromConfig") != nullptr;
46#endif
47}
48
49llvm::Error TryLoad(const char *path) {
50 std::string err_msg;
51 llvm::sys::DynamicLibrary lib =
52 llvm::sys::DynamicLibrary::getPermanentLibrary(path, &err_msg);
53 if (!lib.isValid())
54 return llvm::createStringErrorV("could not load '{0}': {1}", path, err_msg);
55
56 // A successful load does not imply the library is actually libpython,
57 // so reject anything that doesn't expose a stable-ABI symbol.
58 if (!lib.getAddressOfSymbol("Py_IsInitialized"))
59 return llvm::createStringErrorV(
60 "'{0}' does not export Py_IsInitialized; not a Python runtime", path);
61
62 return llvm::Error::success();
63}
64
65llvm::Expected<std::string> LoadPythonRuntime() {
66 if (IsPythonAlreadyLoaded())
67 return std::string();
68
69 llvm::Error failures =
70 llvm::createStringError("could not locate the Python runtime library");
71 std::string loaded;
72
73 auto try_path = [&](const char *path) -> bool {
74 if (llvm::Error err = TryLoad(path)) {
75 failures = llvm::joinErrors(std::move(failures), std::move(err));
76 return false;
77 }
78 loaded = path;
79 return true;
80 };
81
82 if (const char *override_path = std::getenv("LLDB_PYTHON_LIBRARY");
83 override_path && *override_path) {
84 if (try_path(override_path)) {
85 consumeError(std::move(failures));
86 return loaded;
87 }
88 }
89
90#ifdef LLDB_PYTHON_RUNTIME_LIBRARY_BUILD_PATH
91 // The build-time Python is the closest stable-ABI match for what the
92 // plugin was compiled against, so prefer it over the platform fallbacks.
93 if (try_path(LLDB_PYTHON_RUNTIME_LIBRARY_BUILD_PATH)) {
94 consumeError(std::move(failures));
95 return loaded;
96 }
97#endif
98
99 bool found = false;
101 [&](const char *candidate) { return found = try_path(candidate); });
102
103 if (found) {
104 consumeError(std::move(failures));
105 return loaded;
106 }
107 return std::move(failures);
108}
109
110class PythonRuntimeLoader : public ScriptInterpreterRuntimeLoader {
111public:
112 llvm::Error Load() override {
113 EnsureLoaded();
114 if (m_error_message.empty())
115 return llvm::Error::success();
116 return llvm::createStringError(m_error_message);
117 }
118
119 llvm::Expected<llvm::StringRef> GetLoadedPath() override {
120 EnsureLoaded();
121 if (!m_error_message.empty())
122 return llvm::createStringError(m_error_message);
123 return llvm::StringRef(m_path);
124 }
125
126 bool IsLoaded() override { return IsPythonAlreadyLoaded(); }
127
128private:
129 void EnsureLoaded() {
130 llvm::call_once(m_once, [this] {
131 if (llvm::Expected<std::string> result = LoadPythonRuntime()) {
132 m_path = std::move(*result);
133 if (m_path.empty())
134 return;
135#if defined(_WIN32)
136 // liblldb.dll may link against (lib)python3.dll (stable ABI). Ensure
137 // it is loaded from the same directory as the version-specific runtime
138 // so the delay-load resolver can find it.
139 llvm::SmallString<256> stable_abi_path(m_path);
140 llvm::sys::path::remove_filename(stable_abi_path);
141#if defined(__MINGW32__)
142 llvm::sys::path::append(stable_abi_path, "libpython3.dll");
143#else
144 llvm::sys::path::append(stable_abi_path, "python3.dll");
145#endif
146 std::string err;
147 llvm::sys::DynamicLibrary::getPermanentLibrary(stable_abi_path.c_str(),
148 &err);
149#endif
150 } else {
151 m_error_message = llvm::toString(result.takeError());
152 LLDB_LOG(GetLog(LLDBLog::Host), "Python runtime load failed: {0}",
153 m_error_message);
154 }
155 });
156 }
157
158 llvm::once_flag m_once;
159 std::string m_path;
160 std::string m_error_message;
161};
162
163} // namespace
164
165llvm::Expected<ScriptInterpreterRuntimeLoader &>
167 switch (language) {
169 static PythonRuntimeLoader instance;
170 return instance;
171 }
175 return llvm::createStringError(
176 "no runtime loader for the requested script language");
177 }
178 llvm_unreachable("unhandled ScriptLanguage");
179}
180
181} // namespace lldb_private
#define LLDB_LOG(log,...)
The LLDB_LOG* macros defined below are the way to emit log messages.
Definition Log.h:364
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:327
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