QLibrary/Darwin: don't use RTLD_NODELETE, just don't dlclose()
As the comment says, the OS does support the flag and a perusal of the source code for dyld[1] shows it does handle the flag, setting an internal flag called "leaveMapped", which is different from the "neverUnload" flag. My guess is that it literally just leaves the memory for the plugin mapped, but removes all links to it in the dyld internal data structures. That results in the Objective C runtime crashing when running content from NODELETE plugins that has been dlclose()d. This probably explains why lldb stops seeing those symbols too: (lldb) f frame #0: 0x0000000103872eb4 QtCore`QLibraryPrivate::unload_sys(this=0x000060000179c210) at qlibrary_unix.cpp:258:21 -> 258 if (doTryUnload && dlclose(pHnd.loadAcquire())) { (lldb) target modules list libqcocoa.dylib [ 0] 81EB58D5-8D31-333A-9E8C-F2385F6EFCF4 0x00000001043de000 libqcocoa.dylib (lldb) image lookup -vn QCocoaEventDispatcher::QCocoaEventDispatcher 2 matches found in libqcocoa.dylib: Address: libqcocoa.dylib[0x0000000000036480] (libqcocoa.dylib.__TEXT.__text + 190368) Summary: libqcocoa.dylib`QCocoaEventDispatcher::QCocoaEventDispatcher(QObject*) at qcocoaeventdispatcher.mm:776 ... (lldb) n (lldb) target modules list libqcocoa.dylib [ 0] 81EB58D5-8D31-333A-9E8C-F2385F6EFCF4 libqcocoa.dylib[0x0000000000000000] libqcocoa.dylib (lldb) image lookup -vn QCocoaEventDispatcher::QCocoaEventDispatcher [1] https://github.com/apple-oss-distributions/dyld Task-number: QTBUG-135044 Task-number: QTBUG-134080 Task-number: QTBUG-133861 Task-number: QTBUG-132697 Task-number: QTBUG-102984 Task-number: QTBUG-132381 Pick-to: 6.9 6.9.0 6.8 Change-Id: I7da3b3615a6ace7c72d1fffd5cf560d8f8a4e1bb Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
This commit is contained in:
parent
0c0b5d0ff0
commit
1a54a03301
@ -359,6 +359,11 @@ inline void QFactoryLoader::Private::updateSinglePath(const QString &path)
|
|||||||
loadedLibraries.resize(libraries.size());
|
loadedLibraries.resize(libraries.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void QFactoryLoader::setLoadHints(QLibrary::LoadHints loadHints)
|
||||||
|
{
|
||||||
|
d->loadHints = loadHints;
|
||||||
|
}
|
||||||
|
|
||||||
void QFactoryLoader::update()
|
void QFactoryLoader::update()
|
||||||
{
|
{
|
||||||
#ifdef QT_SHARED
|
#ifdef QT_SHARED
|
||||||
@ -549,6 +554,7 @@ inline QObject *QFactoryLoader::instanceHelper_locked(int index) const
|
|||||||
if (size_t(index) < d->libraries.size()) {
|
if (size_t(index) < d->libraries.size()) {
|
||||||
QLibraryPrivate *library = d->libraries[index].get();
|
QLibraryPrivate *library = d->libraries[index].get();
|
||||||
d->loadedLibraries[index] = true;
|
d->loadedLibraries[index] = true;
|
||||||
|
library->setLoadHints(d->loadHints);
|
||||||
return library->pluginInstance();
|
return library->pluginInstance();
|
||||||
}
|
}
|
||||||
// we know d->libraries.size() <= index <= numeric_limits<decltype(index)>::max() → no overflow
|
// we know d->libraries.size() <= index <= numeric_limits<decltype(index)>::max() → no overflow
|
||||||
|
@ -48,6 +48,7 @@ public:
|
|||||||
#if QT_CONFIG(library)
|
#if QT_CONFIG(library)
|
||||||
~QFactoryLoader();
|
~QFactoryLoader();
|
||||||
|
|
||||||
|
void setLoadHints(QLibrary::LoadHints hints);
|
||||||
void update();
|
void update();
|
||||||
static void refreshAll();
|
static void refreshAll();
|
||||||
|
|
||||||
@ -79,6 +80,7 @@ private:
|
|||||||
QString suffix;
|
QString suffix;
|
||||||
QString extraSearchPath;
|
QString extraSearchPath;
|
||||||
Qt::CaseSensitivity cs;
|
Qt::CaseSensitivity cs;
|
||||||
|
QLibrary::LoadHints loadHints;
|
||||||
void updateSinglePath(const QString &pluginDir);
|
void updateSinglePath(const QString &pluginDir);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -14,6 +14,11 @@
|
|||||||
|
|
||||||
#ifdef Q_OS_DARWIN
|
#ifdef Q_OS_DARWIN
|
||||||
# include <private/qcore_mac_p.h>
|
# include <private/qcore_mac_p.h>
|
||||||
|
|
||||||
|
// Apple's dyld *does* support RTLD_NODELETE and the library remains loaded in
|
||||||
|
// memory after the dlclose() call, but their Objective C crashes when running
|
||||||
|
// code from unloaded-but-still-loaded plugins.
|
||||||
|
# undef RTLD_NODELETE
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef Q_OS_ANDROID
|
#ifdef Q_OS_ANDROID
|
||||||
|
@ -144,12 +144,12 @@ int pthread_timedjoin_np(...) { return ENOSYS; } // pretend
|
|||||||
// Finally, for the thread that called ::exit() (which in most cases happens by
|
// Finally, for the thread that called ::exit() (which in most cases happens by
|
||||||
// returning from the main() function), finish() and cleanup() happen at
|
// returning from the main() function), finish() and cleanup() happen at
|
||||||
// function-local static destructor time, and the deref & delete happens later,
|
// function-local static destructor time, and the deref & delete happens later,
|
||||||
// at global static destruction time. This is important so QLibraryStore can
|
// at global static destruction time. That way, we delete the event dispatcher
|
||||||
// unload plugins between those two steps: it is also destroyed by a global
|
// before QLibraryStore's clean up runs and unloads remaining plugins. This
|
||||||
// static destructor, but with a lower priority than ours. The order needs to
|
// strategy was chosen because of crashes observed while running the event
|
||||||
// be this way so we delete the event dispatcher before plugins unload, as
|
// dispatcher's destructor, and though the cause of the crash was something
|
||||||
// often the dispatcher for the main thread is provided by a QPA plugin, but
|
// else (QFactoryLoader always loads with PreventUnloadHint set), other plugins
|
||||||
// the QThreadData object must still be alive when the plugins do unload.
|
// may still attempt to access QThreadData in their global destructors.
|
||||||
|
|
||||||
Q_CONSTINIT static thread_local QThreadData *currentThreadData = nullptr;
|
Q_CONSTINIT static thread_local QThreadData *currentThreadData = nullptr;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user