diff options
Diffstat (limited to 'src/corelib/plugin/qlibrary.cpp')
-rw-r--r-- | src/corelib/plugin/qlibrary.cpp | 157 |
1 files changed, 96 insertions, 61 deletions
diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index 6a3f13bb81..a3ef8e3c52 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -11,12 +11,11 @@ #include <qfile.h> #include <qfileinfo.h> #include <qjsondocument.h> -#include <qmap.h> #include <qmutex.h> #include <qoperatingsystemversion.h> #include <qstringlist.h> -#ifdef Q_OS_MAC +#ifdef Q_OS_DARWIN # include <private/qcore_mac_p.h> #endif #include <private/qcoreapplication_p.h> @@ -30,10 +29,15 @@ #include <qtcore_tracepoints_p.h> +#include <QtCore/q20map.h> + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; +Q_TRACE_POINT(qtcore, QLibraryPrivate_load_entry, const QString &fileName); +Q_TRACE_POINT(qtcore, QLibraryPrivate_load_exit, bool success); + // On Unix systema and on Windows with MinGW, we can mix and match debug and // release plugins without problems. (unless compiled in debug-and-release mode // - why?) @@ -207,7 +211,7 @@ static QLibraryScanResult qt_find_pattern(const char *s, qsizetype s_len, QStrin information could not be read. Returns true if version information is present and successfully read. */ -static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) +static QLibraryScanResult findPatternUnloaded(const QString &library, QLibraryPrivate *lib) { QFile file(library); if (!file.open(QIODevice::ReadOnly)) { @@ -215,7 +219,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) lib->errorString = file.errorString(); qCWarning(qt_lcDebugPlugins, "%ls: cannot open: %ls", qUtf16Printable(library), qUtf16Printable(file.errorString())); - return false; + return {}; } // Files can be bigger than the virtual memory size on 32-bit systems, so @@ -232,7 +236,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) // This can't be used as a plugin. qCWarning(qt_lcDebugPlugins, "%ls: failed to map to memory: %ls", qUtf16Printable(library), qUtf16Printable(file.errorString())); - return false; + return {}; } #else QByteArray data; @@ -249,15 +253,19 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) QString errMsg = library; QLibraryScanResult r = qt_find_pattern(filedata, fdlen, &errMsg); if (r.length) { +#if defined(Q_OF_MACH_O) + if (r.isEncrypted) + return r; +#endif if (!lib->metaData.parse(QByteArrayView(filedata + r.pos, r.length))) { errMsg = lib->metaData.errorString(); - qCWarning(qt_lcDebugPlugins, "Found invalid metadata in lib %ls: %ls", + qCDebug(qt_lcDebugPlugins, "Found invalid metadata in lib %ls: %ls", qUtf16Printable(library), qUtf16Printable(errMsg)); } else { qCDebug(qt_lcDebugPlugins, "Found metadata in lib %ls, metadata=\n%s\n", qUtf16Printable(library), QJsonDocument(lib->metaData.toJson()).toJson().constData()); - return true; + return r; } } else { qCDebug(qt_lcDebugPlugins, "Failed to find metadata in lib %ls: %ls", @@ -266,7 +274,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) lib->errorString = QLibrary::tr("Failed to extract plugin meta data from '%1': %2") .arg(library, errMsg); - return false; + return {}; } static void installCoverageTool(QLibraryPrivate *libPrivate) @@ -314,7 +322,7 @@ private: static inline QLibraryStore *instance(); // all members and instance() are protected by qt_library_mutex - typedef QMap<QString, QLibraryPrivate *> LibraryMap; + typedef std::map<QString, QLibraryPrivate *> LibraryMap; LibraryMap libraryMap; }; @@ -334,19 +342,12 @@ inline void QLibraryStore::cleanup() return; // find any libraries that are still loaded but have a no one attached to them - LibraryMap::Iterator it = data->libraryMap.begin(); - for (; it != data->libraryMap.end(); ++it) { - QLibraryPrivate *lib = it.value(); + for (auto &[_, lib] : data->libraryMap) { if (lib->libraryRefCount.loadRelaxed() == 1) { if (lib->libraryUnloadCount.loadRelaxed() > 0) { Q_ASSERT(lib->pHnd.loadRelaxed()); lib->libraryUnloadCount.storeRelaxed(1); -#ifdef __GLIBC__ - // glibc has a bug in unloading from global destructors - // see https://bugzilla.novell.com/show_bug.cgi?id=622977 - // and http://sourceware.org/bugzilla/show_bug.cgi?id=11941 - lib->unload(QLibraryPrivate::NoUnloadSys); -#elif defined(Q_OS_DARWIN) +#if defined(Q_OS_DARWIN) // We cannot fully unload libraries, as we don't know if there are // lingering references (in system threads e.g.) to Objective-C classes // defined in the library. @@ -355,14 +356,13 @@ inline void QLibraryStore::cleanup() lib->unload(); #endif } - delete lib; - it.value() = nullptr; + delete std::exchange(lib, nullptr); } } // dump all objects that remain if (lcDebugLibrary().isDebugEnabled()) { - for (QLibraryPrivate *lib : qAsConst(data->libraryMap)) { + for (auto &[_, lib] : data->libraryMap) { if (lib) qDebug(lcDebugLibrary) << "On QtCore unload," << lib->fileName << "was leaked, with" @@ -393,24 +393,34 @@ QLibraryStore *QLibraryStore::instance() inline QLibraryPrivate *QLibraryStore::findOrCreate(const QString &fileName, const QString &version, QLibrary::LoadHints loadHints) { + auto lazyNewLib = [&] { + auto result = new QLibraryPrivate(fileName, version, loadHints); + result->libraryRefCount.ref(); + return result; + }; + + if (fileName.isEmpty()) // request for empty d-pointer in QLibrary::setLoadHints(); + return lazyNewLib(); // must return an independent (new) object + QMutexLocker locker(&qt_library_mutex); QLibraryStore *data = instance(); - // check if this library is already loaded - QLibraryPrivate *lib = nullptr; - if (Q_LIKELY(data)) { - lib = data->libraryMap.value(fileName); - if (lib) - lib->mergeLoadHints(loadHints); + if (Q_UNLIKELY(!data)) { + locker.unlock(); + return lazyNewLib(); } - if (!lib) - lib = new QLibraryPrivate(fileName, version, loadHints); - // track this library - if (Q_LIKELY(data) && !fileName.isEmpty()) - data->libraryMap.insert(fileName, lib); + QString mapName = version.isEmpty() ? fileName : fileName + u'\0' + version; + + QLibraryPrivate *&lib = data->libraryMap[std::move(mapName)]; + if (lib) { + // already loaded + lib->libraryRefCount.ref(); + lib->mergeLoadHints(loadHints); + } else { + lib = lazyNewLib(); + } - lib->libraryRefCount.ref(); return lib; } @@ -428,9 +438,12 @@ inline void QLibraryStore::releaseLibrary(QLibraryPrivate *lib) Q_ASSERT(lib->libraryUnloadCount.loadRelaxed() == 0); if (Q_LIKELY(data) && !lib->fileName.isEmpty()) { - QLibraryPrivate *that = data->libraryMap.take(lib->fileName); - Q_ASSERT(lib == that); - Q_UNUSED(that); + using q20::erase_if; + const auto n = erase_if(data->libraryMap, [lib](const auto &e) { + return e.second == lib; + }); + Q_ASSERT_X(n, "~QLibrary", "Did not find this library in the library map"); + Q_UNUSED(n); } delete lib; } @@ -459,7 +472,7 @@ void QLibraryPrivate::mergeLoadHints(QLibrary::LoadHints lh) if (pHnd.loadRelaxed()) return; - loadHintsInt.storeRelaxed(lh.toInt()); + loadHintsInt.fetchAndOrRelaxed(lh.toInt()); } QFunctionPointer QLibraryPrivate::resolve(const char *symbol) @@ -471,6 +484,13 @@ QFunctionPointer QLibraryPrivate::resolve(const char *symbol) void QLibraryPrivate::setLoadHints(QLibrary::LoadHints lh) { + // Set the load hints directly for a dummy if this object is not associated + // with a file. Such object is not shared between multiple instances. + if (fileName.isEmpty()) { + loadHintsInt.storeRelaxed(lh.toInt()); + return; + } + // this locks a global mutex QMutexLocker lock(&qt_library_mutex); mergeLoadHints(lh); @@ -706,7 +726,7 @@ void QLibraryPrivate::updatePluginState() bool success = false; -#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) +#if defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) if (fileName.endsWith(".debug"_L1)) { // refuse to load a file that ends in .debug // these are the debug symbols from the libraries @@ -722,7 +742,22 @@ void QLibraryPrivate::updatePluginState() if (!pHnd.loadRelaxed()) { // scan for the plugin metadata without loading - success = findPatternUnloaded(fileName, this); + QLibraryScanResult result = findPatternUnloaded(fileName, this); +#if defined(Q_OF_MACH_O) + if (result.length && result.isEncrypted) { + // We found the .qtmetadata section, but since the library is encrypted + // we need to dlopen() it before we can parse the metadata for further + // validation. + qCDebug(qt_lcDebugPlugins, "Library is encrypted. Doing prospective load before parsing metadata"); + locker.unlock(); + load(); + locker.relock(); + success = qt_get_metadata(this, &errorString); + } else +#endif + { + success = result.length != 0; + } } else { // library is already loaded (probably via QLibrary) // simply get the target function and call it. @@ -745,7 +780,7 @@ void QLibraryPrivate::updatePluginState() uint qt_version = uint(metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger()); bool debug = metaData.value(QtPluginMetaDataKeys::IsDebug).toBool(); if ((qt_version & 0x00ff00) > (QT_VERSION & 0x00ff00) || (qt_version & 0xff0000) != (QT_VERSION & 0xff0000)) { - qCWarning(qt_lcDebugPlugins, "In %s:\n" + qCDebug(qt_lcDebugPlugins, "In %s:\n" " Plugin uses incompatible Qt library (%d.%d.%d) [%s]", QFile::encodeName(fileName).constData(), (qt_version&0xff0000) >> 16, (qt_version&0xff00) >> 8, qt_version&0xff, @@ -781,9 +816,11 @@ bool QLibrary::load() return false; if (d.tag() == Loaded) return d->pHnd.loadRelaxed(); - else + if (d->load()) { d.setTag(Loaded); - return d->load(); + return true; + } + return false; } /*! @@ -797,7 +834,9 @@ bool QLibrary::load() call will fail, and unloading will only happen when every instance has called unload(). - Note that on Mac OS X 10.3 (Panther), dynamic libraries cannot be unloaded. + Note that on \macos, dynamic libraries cannot be unloaded. + QLibrary::unload() will return \c true, but the library will remain + loaded into the process. \sa resolve(), load() */ @@ -811,13 +850,17 @@ bool QLibrary::unload() } /*! - Returns \c true if the library is loaded; otherwise returns \c false. + Returns \c true if load() succeeded; otherwise returns \c false. + + \note Prior to Qt 6.6, this function would return \c true even without a + call to load() if another QLibrary object on the same library had caused it + to be loaded. \sa load() */ bool QLibrary::isLoaded() const { - return d && d->pHnd.loadRelaxed(); + return d.tag() == Loaded; } @@ -911,13 +954,7 @@ QLibrary::~QLibrary() void QLibrary::setFileName(const QString &fileName) { - QLibrary::LoadHints lh; - if (d) { - lh = d->loadHints(); - d->release(); - d = {}; - } - d = QLibraryPrivate::findOrCreate(fileName, QString(), lh); + setFileNameAndVersion(fileName, QString()); } QString QLibrary::fileName() const @@ -940,13 +977,7 @@ QString QLibrary::fileName() const */ void QLibrary::setFileNameAndVersion(const QString &fileName, int verNum) { - QLibrary::LoadHints lh; - if (d) { - lh = d->loadHints(); - d->release(); - d = {}; - } - d = QLibraryPrivate::findOrCreate(fileName, verNum >= 0 ? QString::number(verNum) : QString(), lh); + setFileNameAndVersion(fileName, verNum >= 0 ? QString::number(verNum) : QString()); } /*! @@ -964,9 +995,9 @@ void QLibrary::setFileNameAndVersion(const QString &fileName, const QString &ver if (d) { lh = d->loadHints(); d->release(); - d = {}; } - d = QLibraryPrivate::findOrCreate(fileName, version, lh); + QLibraryPrivate *dd = QLibraryPrivate::findOrCreate(fileName, version, lh); + d = QTaggedPointer(dd, NotLoaded); // we haven't load()ed } /*! @@ -1101,6 +1132,10 @@ QString QLibrary::errorString() const lazy symbol resolution, and will not export external symbols for resolution in other dynamically-loaded libraries. + \note Hints can only be cleared when this object is not associated with a + file. Hints can only be added once the file name is set (\a hints will + be or'ed with the old hints). + \note Setting this property after the library has been loaded has no effect and loadHints() will not reflect those changes. |