| //===----------------------------------------------------------------------===// |
| // |
| // 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/Config.h" |
| #include "lldb/Host/ScriptInterpreterRuntimeLoader.h" |
| |
| #include "PythonRuntimeLoaderInternal.h" |
| #include "lldb/Utility/LLDBLog.h" |
| #include "lldb/Utility/Log.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/Support/DynamicLibrary.h" |
| #include "llvm/Support/ErrorExtras.h" |
| #include "llvm/Support/Path.h" |
| #include "llvm/Support/Threading.h" |
| |
| #include <cstdlib> |
| #include <string> |
| |
| #if defined(_WIN32) |
| #include "lldb/Host/windows/windows.h" |
| #else |
| #include <dlfcn.h> |
| #endif |
| |
| namespace lldb_private { |
| |
| namespace { |
| |
| /// True if libpython is currently mapped into the process. A single-symbol |
| /// probe can match an incompatible runtime that happens to export it, so |
| /// pair an old symbol with one introduced in 3.8 to bound the version on |
| /// both ends. |
| bool IsPythonAlreadyLoaded() { |
| #if defined(_WIN32) |
| HMODULE main = ::GetModuleHandleW(nullptr); |
| return ::GetProcAddress(main, "Py_IsInitialized") != nullptr && |
| ::GetProcAddress(main, "Py_InitializeFromConfig") != nullptr; |
| #else |
| return ::dlsym(RTLD_DEFAULT, "Py_IsInitialized") != nullptr && |
| ::dlsym(RTLD_DEFAULT, "Py_InitializeFromConfig") != nullptr; |
| #endif |
| } |
| |
| llvm::Error TryLoad(const char *path) { |
| std::string err_msg; |
| llvm::sys::DynamicLibrary lib = |
| llvm::sys::DynamicLibrary::getPermanentLibrary(path, &err_msg); |
| if (!lib.isValid()) |
| return llvm::createStringErrorV("could not load '{0}': {1}", path, err_msg); |
| |
| // A successful load does not imply the library is actually libpython, |
| // so reject anything that doesn't expose a stable-ABI symbol. |
| if (!lib.getAddressOfSymbol("Py_IsInitialized")) |
| return llvm::createStringErrorV( |
| "'{0}' does not export Py_IsInitialized; not a Python runtime", path); |
| |
| return llvm::Error::success(); |
| } |
| |
| llvm::Expected<std::string> LoadPythonRuntime() { |
| if (IsPythonAlreadyLoaded()) |
| return std::string(); |
| |
| llvm::Error failures = |
| llvm::createStringError("could not locate the Python runtime library"); |
| std::string loaded; |
| |
| auto try_path = [&](const char *path) -> bool { |
| if (llvm::Error err = TryLoad(path)) { |
| failures = llvm::joinErrors(std::move(failures), std::move(err)); |
| return false; |
| } |
| loaded = path; |
| return true; |
| }; |
| |
| if (const char *override_path = std::getenv("LLDB_PYTHON_LIBRARY"); |
| override_path && *override_path) { |
| if (try_path(override_path)) { |
| consumeError(std::move(failures)); |
| return loaded; |
| } |
| } |
| |
| #ifdef LLDB_PYTHON_RUNTIME_LIBRARY_BUILD_PATH |
| // The build-time Python is the closest stable-ABI match for what the |
| // plugin was compiled against, so prefer it over the platform fallbacks. |
| if (try_path(LLDB_PYTHON_RUNTIME_LIBRARY_BUILD_PATH)) { |
| consumeError(std::move(failures)); |
| return loaded; |
| } |
| #endif |
| |
| bool found = false; |
| ForEachPythonRuntimeCandidate( |
| [&](const char *candidate) { return found = try_path(candidate); }); |
| |
| if (found) { |
| consumeError(std::move(failures)); |
| return loaded; |
| } |
| return std::move(failures); |
| } |
| |
| class PythonRuntimeLoader : public ScriptInterpreterRuntimeLoader { |
| public: |
| llvm::Error Load() override { |
| EnsureLoaded(); |
| if (m_error_message.empty()) |
| return llvm::Error::success(); |
| return llvm::createStringError(m_error_message); |
| } |
| |
| llvm::Expected<llvm::StringRef> GetLoadedPath() override { |
| EnsureLoaded(); |
| if (!m_error_message.empty()) |
| return llvm::createStringError(m_error_message); |
| return llvm::StringRef(m_path); |
| } |
| |
| bool IsLoaded() override { return IsPythonAlreadyLoaded(); } |
| |
| private: |
| void EnsureLoaded() { |
| llvm::call_once(m_once, [this] { |
| if (llvm::Expected<std::string> result = LoadPythonRuntime()) { |
| m_path = std::move(*result); |
| if (m_path.empty()) |
| return; |
| #if defined(_WIN32) |
| // liblldb.dll may link against (lib)python3.dll (stable ABI). Ensure |
| // it is loaded from the same directory as the version-specific runtime |
| // so the delay-load resolver can find it. |
| llvm::SmallString<256> stable_abi_path(m_path); |
| llvm::sys::path::remove_filename(stable_abi_path); |
| #if defined(__MINGW32__) |
| llvm::sys::path::append(stable_abi_path, "libpython3.dll"); |
| #else |
| llvm::sys::path::append(stable_abi_path, "python3.dll"); |
| #endif |
| std::string err; |
| llvm::sys::DynamicLibrary::getPermanentLibrary(stable_abi_path.c_str(), |
| &err); |
| #endif |
| } else { |
| m_error_message = llvm::toString(result.takeError()); |
| LLDB_LOG(GetLog(LLDBLog::Host), "Python runtime load failed: {0}", |
| m_error_message); |
| } |
| }); |
| } |
| |
| llvm::once_flag m_once; |
| std::string m_path; |
| std::string m_error_message; |
| }; |
| |
| } // namespace |
| |
| llvm::Expected<ScriptInterpreterRuntimeLoader &> |
| ScriptInterpreterRuntimeLoader::Get(lldb::ScriptLanguage language) { |
| switch (language) { |
| case lldb::eScriptLanguagePython: { |
| static PythonRuntimeLoader instance; |
| return instance; |
| } |
| case lldb::eScriptLanguageLua: |
| case lldb::eScriptLanguageNone: |
| case lldb::eScriptLanguageUnknown: |
| return llvm::createStringError( |
| "no runtime loader for the requested script language"); |
| } |
| llvm_unreachable("unhandled ScriptLanguage"); |
| } |
| |
| } // namespace lldb_private |