77 lines
3.4 KiB
Diff
77 lines
3.4 KiB
Diff
From 8a8548de607e0488fd4b8ab05373749545a29342 Mon Sep 17 00:00:00 2001
|
|
From: Mark Johnston <markj@FreeBSD.org>
|
|
Date: Thu, 24 Aug 2023 17:43:36 -0400
|
|
Subject: [PATCH] python bindings: Load all modules with RTLD_GLOBAL
|
|
|
|
libdnf's python bindings are implemented by a set of C++ shared objects
|
|
generated by swig. Some generated code is duplicated between modules,
|
|
in particular the SwigPyIterator class templates, which use exceptions
|
|
of type swig::stop_iteration to signal an end-of-iteration condition.
|
|
The modules do not depend on each other and thus belong to different
|
|
DAGs from the perspective of the runtime linker.
|
|
|
|
It turns out that this stop_iteration exception can be thrown between
|
|
modules. This happens at least during dnf startup with python 3.9:
|
|
|
|
cli.py(935): subst.update_from_etc(from_root, varsdir=conf._get_value('varsdir'))
|
|
--- modulename: config, funcname: _get_value
|
|
config.py(102): method = getattr(self._config, name, None)
|
|
config.py(103): if method is None:
|
|
config.py(105): return method().getValue()
|
|
--- modulename: conf, funcname: varsdir
|
|
conf.py(1183): return _conf.ConfigMain_varsdir(self)
|
|
--- modulename: conf, funcname: getValue
|
|
conf.py(512): return _conf.OptionStringList_getValue(self)
|
|
--- modulename: substitutions, funcname: update_from_etc
|
|
substitutions.py(47): for vars_path in varsdir:
|
|
--- modulename: module, funcname: __iter__
|
|
module.py(557): return self.iterator()
|
|
--- modulename: module, funcname: iterator
|
|
module.py(555): return _module.VectorString_iterator(self)
|
|
--- modulename: transaction, funcname: __next__
|
|
transaction.py(94): return _transaction.SwigPyIterator___next__(self)
|
|
|
|
In particular, the module and transaction modules are somehow both
|
|
involved: module returns the iterator, and transaction advances the
|
|
iterator. Both modules contain the same iterator code, so I'm not sure
|
|
why it works this way. The behaviour is sensitive to import order; for
|
|
example, if transaction is imported before module, then the code above
|
|
ends up using module's implementation of SwigPyItreator___next__.
|
|
|
|
In any case, the use of swig::stop_iteration is broken in the above
|
|
scenario since the exception is thrown by module with module.so's copy
|
|
of the swig::stop_iteration type info, and caught by transaction.so
|
|
using transaction.so's copy of the type info, resulting in an uncaught
|
|
exception.
|
|
|
|
Work around the problem by loading all modules with RTLD_GLOBAL to
|
|
ensure that RTTI is unique. This is required when throwing exceptions
|
|
across DSO boundaries, see https://gcc.gnu.org/faq.html#dso for example.
|
|
|
|
Conflict:NA
|
|
Reference:https://github.com/rpm-software-management/libdnf/commit/8a8548de607e0488fd4b8ab05373749545a29342
|
|
---
|
|
bindings/python/__init__.py | 5 ++++-
|
|
1 file changed, 4 insertions(+), 1 deletion(-)
|
|
|
|
diff --git a/bindings/python/__init__.py b/bindings/python/__init__.py
|
|
index 3bfd3a9441..07cf1959d5 100644
|
|
--- a/bindings/python/__init__.py
|
|
+++ b/bindings/python/__init__.py
|
|
@@ -6,11 +6,14 @@
|
|
import sys, os
|
|
sys.setdlopenflags(os.RTLD_NOW | os.RTLD_GLOBAL)
|
|
from . import error
|
|
-sys.setdlopenflags(os.RTLD_NOW)
|
|
|
|
+# Other modules also need to be loaded with RTLD_GLOBAL to preserve uniqueness
|
|
+# of RTTI. There are code paths where an exception thrown in one module is
|
|
+# supposed to be caught in another.
|
|
from . import common_types
|
|
from . import conf
|
|
from . import module
|
|
from . import repo
|
|
from . import transaction
|
|
from . import utils
|
|
+sys.setdlopenflags(os.RTLD_NOW)
|