================ @@ -0,0 +1,162 @@ +//===----------------------------------------------------------------------===// +// +// 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/Support/DynamicLibrary.h" +#include "llvm/Support/ErrorExtras.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); + } 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) { ---------------- chsigg wrote:
Should this implementation be wrapped in `#if LLDB_ENABLE_PYTHON`? https://github.com/llvm/llvm-project/pull/200524 _______________________________________________ lldb-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/lldb-commits
