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());
|
||||
}
|
||||
|
||||
void QFactoryLoader::setLoadHints(QLibrary::LoadHints loadHints)
|
||||
{
|
||||
d->loadHints = loadHints;
|
||||
}
|
||||
|
||||
void QFactoryLoader::update()
|
||||
{
|
||||
#ifdef QT_SHARED
|
||||
@ -549,6 +554,7 @@ inline QObject *QFactoryLoader::instanceHelper_locked(int index) const
|
||||
if (size_t(index) < d->libraries.size()) {
|
||||
QLibraryPrivate *library = d->libraries[index].get();
|
||||
d->loadedLibraries[index] = true;
|
||||
library->setLoadHints(d->loadHints);
|
||||
return library->pluginInstance();
|
||||
}
|
||||
// we know d->libraries.size() <= index <= numeric_limits<decltype(index)>::max() → no overflow
|
||||
|
@ -48,6 +48,7 @@ public:
|
||||
#if QT_CONFIG(library)
|
||||
~QFactoryLoader();
|
||||
|
||||
void setLoadHints(QLibrary::LoadHints hints);
|
||||
void update();
|
||||
static void refreshAll();
|
||||
|
||||
@ -79,6 +80,7 @@ private:
|
||||
QString suffix;
|
||||
QString extraSearchPath;
|
||||
Qt::CaseSensitivity cs;
|
||||
QLibrary::LoadHints loadHints;
|
||||
void updateSinglePath(const QString &pluginDir);
|
||||
#endif
|
||||
|
||||
|
@ -14,6 +14,11 @@
|
||||
|
||||
#ifdef Q_OS_DARWIN
|
||||
# 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
|
||||
|
||||
#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
|
||||
// returning from the main() function), finish() and cleanup() happen at
|
||||
// function-local static destructor time, and the deref & delete happens later,
|
||||
// at global static destruction time. This is important so QLibraryStore can
|
||||
// unload plugins between those two steps: it is also destroyed by a global
|
||||
// static destructor, but with a lower priority than ours. The order needs to
|
||||
// be this way so we delete the event dispatcher before plugins unload, as
|
||||
// often the dispatcher for the main thread is provided by a QPA plugin, but
|
||||
// the QThreadData object must still be alive when the plugins do unload.
|
||||
// at global static destruction time. That way, we delete the event dispatcher
|
||||
// before QLibraryStore's clean up runs and unloads remaining plugins. This
|
||||
// strategy was chosen because of crashes observed while running the event
|
||||
// dispatcher's destructor, and though the cause of the crash was something
|
||||
// else (QFactoryLoader always loads with PreventUnloadHint set), other plugins
|
||||
// may still attempt to access QThreadData in their global destructors.
|
||||
|
||||
Q_CONSTINIT static thread_local QThreadData *currentThreadData = nullptr;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user