diff --git a/release/CMakeLists.txt b/release/CMakeLists.txt index f77d49dc7..0fb250db8 100644 --- a/release/CMakeLists.txt +++ b/release/CMakeLists.txt @@ -41,7 +41,7 @@ set(CPACK_DEBIAN_PACKAGE_DESCRIPTION "${CPACK_PACKAGE_DESCRIPTION_SUMMARY} applications driver by real-time connected data.") # Add `openssl` package to dependencies list. Used to generate SSL certificates. # We also depend on `python3` because we embed it in Memgraph. -set(CPACK_DEBIAN_PACKAGE_DEPENDS "openssl (>= 1.1.0), python3 (>= 3.5.0)") +set(CPACK_DEBIAN_PACKAGE_DEPENDS "openssl (>= 1.1.0), python3 (>= 3.5.0), libstdc++6") # Setting arhitecture extension for rpm packages set(MG_ARCH_EXTENSION_RPM "noarch") @@ -67,7 +67,7 @@ It aims to deliver developers the speed, simplicity and scale required to build the next generation of applications driver by real-time connected data.") # Add `openssl` package to dependencies list. Used to generate SSL certificates. # We also depend on `python3` because we embed it in Memgraph. -set(CPACK_RPM_PACKAGE_REQUIRES "openssl >= 1.0.0, curl >= 7.29.0, python3 >= 3.5.0") +set(CPACK_RPM_PACKAGE_REQUIRES "openssl >= 1.0.0, curl >= 7.29.0, python3 >= 3.5.0, libstdc >= 6") # All variables must be set before including. include(CPack) diff --git a/src/memgraph.cpp b/src/memgraph.cpp index 0554eca8b..f0872f647 100644 --- a/src/memgraph.cpp +++ b/src/memgraph.cpp @@ -1070,6 +1070,22 @@ int main(int argc, char **argv) { if (maybe_exc) { spdlog::error(memgraph::utils::MessageWithLink("Unable to load support for embedded Python: {}.", *maybe_exc, "https://memgr.ph/python")); + } else { + // Change how we load dynamic libraries on Python by using RTLD_NOW and + // RTLD_DEEPBIND flags. This solves an issue with using the wrong version of + // libstd. + auto gil = memgraph::py::EnsureGIL(); + // NOLINTNEXTLINE(hicpp-signed-bitwise) + auto *flag = PyLong_FromLong(RTLD_NOW | RTLD_DEEPBIND); + auto *setdl = PySys_GetObject("setdlopenflags"); + MG_ASSERT(setdl); + auto *arg = PyTuple_New(1); + MG_ASSERT(arg); + MG_ASSERT(PyTuple_SetItem(arg, 0, flag) == 0); + PyObject_CallObject(setdl, arg); + Py_DECREF(flag); + Py_DECREF(setdl); + Py_DECREF(arg); } } else { spdlog::error( diff --git a/src/query/procedure/module.cpp b/src/query/procedure/module.cpp index af3837048..5c912aaf0 100644 --- a/src/query/procedure/module.cpp +++ b/src/query/procedure/module.cpp @@ -822,7 +822,8 @@ bool SharedLibraryModule::Load(const std::filesystem::path &file_path) { spdlog::info("Loading module {}...", file_path); file_path_ = file_path; dlerror(); // Clear any existing error. - handle_ = dlopen(file_path.c_str(), RTLD_NOW | RTLD_LOCAL); + // NOLINTNEXTLINE(hicpp-signed-bitwise) + handle_ = dlopen(file_path.c_str(), RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND); if (!handle_) { spdlog::error( utils::MessageWithLink("Unable to load module {}; {}.", file_path, dlerror(), "https://memgr.ph/modules")); diff --git a/src/query/procedure/module.hpp b/src/query/procedure/module.hpp index c3403b636..17a62d738 100644 --- a/src/query/procedure/module.hpp +++ b/src/query/procedure/module.hpp @@ -13,6 +13,7 @@ /// API for loading and registering modules providing custom oC procedures #pragma once +#include #include #include #include @@ -128,6 +129,40 @@ class ModuleRegistry final { const std::filesystem::path &InternalModuleDir() const noexcept; private: + class SharedLibraryHandle { + public: + SharedLibraryHandle(const std::string &shared_library, int mode) : handle_{dlopen(shared_library.c_str(), mode)} {} + SharedLibraryHandle(const SharedLibraryHandle &) = delete; + SharedLibraryHandle(SharedLibraryHandle &&) = delete; + SharedLibraryHandle operator=(const SharedLibraryHandle &) = delete; + SharedLibraryHandle operator=(SharedLibraryHandle &&) = delete; + + ~SharedLibraryHandle() { + if (handle_) { + dlclose(handle_); + } + } + + private: + void *handle_; + }; + +#if __has_feature(address_sanitizer) + // This is why we need RTLD_NODELETE and we must not use RTLD_DEEPBIND with + // ASAN: https://github.com/google/sanitizers/issues/89 + SharedLibraryHandle libstd_handle{"libstdc++.so.6", RTLD_NOW | RTLD_LOCAL | RTLD_NODELETE}; +#else + // The reason behind opening share library during runtime is to avoid issues + // with loading symbols from stdlib. We have encounter issues with locale + // that cause std::cout not being printed and issues when python libraries + // would call stdlib (e.g. pytorch). + // The way that those issues were solved was + // by using RTLD_DEEPBIND. RTLD_DEEPBIND ensures that the lookup for the + // mentioned library will be first performed in the already existing binded + // libraries and then the global namespace. + // RTLD_DEEPBIND => https://linux.die.net/man/3/dlopen + SharedLibraryHandle libstd_handle{"libstdc++.so.6", RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND}; +#endif std::vector modules_dirs_; std::filesystem::path internal_module_dir_; };