Skip to content

Commit

Permalink
python: introduce MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL.
Browse files Browse the repository at this point in the history
If enabled, this causes sys.setdlopenflags() being called with
RTLD_GLOBAL before the native Corrade module is loaded, in a hope to
resolve recurring nightmares with static Corrade and Magnum libraries
being linked into multiple dynamic modules.
  • Loading branch information
mosra committed Jan 16, 2024
1 parent 37b0229 commit b4d437b
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 4 deletions.
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ endif()

option(MAGNUM_BUILD_TESTS "Build unit tests" OFF)

if(MAGNUM_BUILD_STATIC AND UNIX)
# See src/python/corrade/__init__.py.in and the corresponding CMakeLists
# for details
option(MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL "Build Python bindings linking to static libraries with RTLD_GLOBAL enabled for loading" ON)
endif()

# Backwards compatibility for unprefixed CMake options. If the user isn't
# explicitly using prefixed options in the first run already, accept the
# unprefixed options, and remember this decision for subsequent runs
Expand Down
21 changes: 19 additions & 2 deletions doc/python/pages/building.rst
Original file line number Diff line number Diff line change
Expand Up @@ -150,14 +150,31 @@ containing location of all built libraries for use with Python setuptools:

In case Corrade or Magnum is built with :dox:`CORRADE_BUILD_STATIC` /
:dox:`MAGNUM_BUILD_STATIC`, the corresponding bindings are compiled into a
single dynamic module instead of one module per Corrade/Magnum library. In this
case, similarly to linking static plugins to Magnum's own command-line
single dynamic module instead of one module per Corrade/Magnum library.

In this case, similarly to linking static plugins to Magnum's own command-line
utilities, you can use the ``MAGNUM_PYTHON_BINDINGS_STATIC_PLUGINS`` CMake
variable to link static plugins to the Python module, assuming Magnum, Magnum
Plugins and Magnum Bindings are all CMake subprojects. It's a
semicolon-separated list of existing CMake targets, for example
``Magnum::AnyImageImporter;MagnumPlugins::StbImageImporter``.

On Unix platforms, Python by default loads the modules in isolated namespaces.
That's a good thing to do from a security point of view, nevertheless with
static builds of Corrade and Magnum it will result in globals duplicated across
the Corrade and Magnum modules (and also any other Python modules that
use Magnum natively inside) unable to see each other in order to deduplicate
themselves, causing strange issues. To solve that, the
``MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL`` CMake option, which is enabled by
default on static builds on Unix platforms, overrides Python's module loading
code to not load them in isolated namespaces using :ref:`sys.setdlopenflags()`.
The override then happens inside :py:`import corrade` and is in effect for the
rest of the interpreter lifetime, to affect also any potential other modules
depending on Magnum loaded later. See also
:dox:`CORRADE_BUILD_STATIC_UNIQUE_GLOBALS` and
:dox:`MAGNUM_BUILD_STATIC_UNIQUE_GLOBALS` in Corrade and Magnum for more
information.

`Running unit tests`_
---------------------

Expand Down
6 changes: 6 additions & 0 deletions doc/python/pages/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ Changelog
- Added a ``MAGNUM_PYTHON_BINDINGS_STATIC_PLUGINS`` CMake option for linking
static plugins to the Python bindings module. See the
:ref:`building documentation <std:doc:building>` for more information.
- Added a ``MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL`` CMake option to make
the Python bindings module loaded into the global namespace instead of
isolated in order to attempt to solve problems with duplicated globals when
static builds of Corrade and Magnum are linked into multiple dynamic
modules. See the :ref:`building documentation <std:doc:building>` for more
information.

`2020.06`_
==========
Expand Down
16 changes: 14 additions & 2 deletions src/python/corrade/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ if(NOT CORRADE_BUILD_STATIC)

# Otherwise put it all into one library so it's easier to install (which is the
# point of static builds). It also nicely avoids problems with multiply-defined
# global data.
# global data. Unless the static libraries are linked into multiple Python
# modules, that is, which is (on Unix at least) attempted to be solved by the
# MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL option below.
else()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/staticconfigure.h.cmake
${CMAKE_CURRENT_BINARY_DIR}/staticconfigure.h)
Expand All @@ -102,6 +104,16 @@ else()
endif()
endif()

# If the option is enabled, the setdlopenflags() code in __init__.py is used,
# otherwise it's commented out
if(MAGNUM_BUILD_STATIC AND UNIX AND MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL)
set(_MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL "")
else()
set(_MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL "## ")
endif()
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/__init__.py.in
${CMAKE_CURRENT_BINARY_DIR}/__init__.py)

pybind11_add_module(corrade ${pybind11_add_module_SYSTEM} ${corrade_SRCS})
target_include_directories(corrade PRIVATE
${PROJECT_SOURCE_DIR}/src
Expand All @@ -113,7 +125,7 @@ set_target_properties(corrade PROPERTIES
LIBRARY_OUTPUT_DIRECTORY ${output_dir})

file(GENERATE OUTPUT ${output_dir}/corrade/__init__.py
INPUT ${CMAKE_CURRENT_SOURCE_DIR}/__init__.py)
INPUT ${CMAKE_CURRENT_BINARY_DIR}/__init__.py)

if(MAGNUM_BUILD_TESTS)
add_subdirectory(test)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@

"""Root Corrade module"""

# If MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL is enabled in CMake, the lines
# below are included, otherwise they are commented out. What it does is a
# futile attempt to make Corrade (and Magnum) built as static libraries work
# together when built into multiple dynamic Python modules.
${_MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL}import sys
${_MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL}import ctypes
${_MAGNUM_BUILD_PYTHON_BINDINGS_RTLD_GLOBAL}sys.setdlopenflags(sys.getdlopenflags()|ctypes.RTLD_GLOBAL)

# On Windows, if a known directory layout is detected, add paths containing
# binaries to the DLL search path
import platform
Expand Down Expand Up @@ -59,3 +67,5 @@
# to globals and this would likely cause conflicts (magnum also defines
# BUILD_*)
]

# kate: hl python

0 comments on commit b4d437b

Please sign in to comment.