diff options
Diffstat (limited to 'src/corelib/plugin')
-rw-r--r-- | src/corelib/plugin/qcoffpeparser.cpp | 2 | ||||
-rw-r--r-- | src/corelib/plugin/qelfparser_p.cpp | 30 | ||||
-rw-r--r-- | src/corelib/plugin/qfactoryinterface.h | 2 | ||||
-rw-r--r-- | src/corelib/plugin/qfactoryloader.cpp | 280 | ||||
-rw-r--r-- | src/corelib/plugin/qfactoryloader_p.h | 5 | ||||
-rw-r--r-- | src/corelib/plugin/qlibrary.cpp | 159 | ||||
-rw-r--r-- | src/corelib/plugin/qlibrary_p.h | 11 | ||||
-rw-r--r-- | src/corelib/plugin/qlibrary_unix.cpp | 63 | ||||
-rw-r--r-- | src/corelib/plugin/qlibrary_win.cpp | 2 | ||||
-rw-r--r-- | src/corelib/plugin/qmachparser.cpp | 25 | ||||
-rw-r--r-- | src/corelib/plugin/qplugin.h | 8 | ||||
-rw-r--r-- | src/corelib/plugin/qplugin.qdoc | 9 | ||||
-rw-r--r-- | src/corelib/plugin/qpluginloader.cpp | 40 | ||||
-rw-r--r-- | src/corelib/plugin/quuid.cpp | 279 | ||||
-rw-r--r-- | src/corelib/plugin/quuid.h | 221 |
15 files changed, 829 insertions, 307 deletions
diff --git a/src/corelib/plugin/qcoffpeparser.cpp b/src/corelib/plugin/qcoffpeparser.cpp index 054a433603..639e402a07 100644 --- a/src/corelib/plugin/qcoffpeparser.cpp +++ b/src/corelib/plugin/qcoffpeparser.cpp @@ -117,7 +117,7 @@ Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, HeaderDebug h) switch (h.h->FileHeader.Machine) { case IMAGE_FILE_MACHINE_I386: d << "i386"; break; case IMAGE_FILE_MACHINE_ARM: d << "ARM"; break; - case IMAGE_FILE_MACHINE_ARMNT: d << "ARM Thumb-2"; break;; + case IMAGE_FILE_MACHINE_ARMNT: d << "ARM Thumb-2"; break; case IMAGE_FILE_MACHINE_THUMB: d << "Thumb"; break; case IMAGE_FILE_MACHINE_IA64: d << "IA-64"; break; case IMAGE_FILE_MACHINE_MIPS16: d << "MIPS16"; break; diff --git a/src/corelib/plugin/qelfparser_p.cpp b/src/corelib/plugin/qelfparser_p.cpp index b0c23d58b6..78c9be0e56 100644 --- a/src/corelib/plugin/qelfparser_p.cpp +++ b/src/corelib/plugin/qelfparser_p.cpp @@ -35,7 +35,7 @@ static constexpr bool IncludeValidityChecks = true; # define QELFPARSER_DEBUG #endif #if defined(QELFPARSER_DEBUG) -static Q_LOGGING_CATEGORY(lcElfParser, "qt.core.plugin.elfparser") +Q_STATIC_LOGGING_CATEGORY(lcElfParser, "qt.core.plugin.elfparser") # define qEDebug qCDebug(lcElfParser) << reinterpret_cast<const char16_t *>(error.errMsg->constData()) << ':' #else # define qEDebug if (false) {} else QNoDebug() @@ -54,6 +54,10 @@ static Q_LOGGING_CATEGORY(lcElfParser, "qt.core.plugin.elfparser") # define PT_GNU_PROPERTY 0x6474e553 #endif +#ifndef PN_XNUM +# define PN_XNUM 0xffff +#endif + QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wunused-const-variable") @@ -107,14 +111,22 @@ struct ElfMachineCheck static const Elf32_Half ExpectedMachine = #if 0 // nothing +#elif defined(Q_PROCESSOR_ALPHA) + EM_ALPHA #elif defined(Q_PROCESSOR_ARM_32) EM_ARM #elif defined(Q_PROCESSOR_ARM_64) EM_AARCH64 #elif defined(Q_PROCESSOR_BLACKFIN) EM_BLACKFIN +#elif defined(Q_PROCESSOR_HPPA) + EM_PARISC #elif defined(Q_PROCESSOR_IA64) EM_IA_64 +#elif defined(Q_PROCESSOR_LOONGARCH) + EM_LOONGARCH +#elif defined(Q_PROCESSOR_M68K) + EM_68K #elif defined(Q_PROCESSOR_MIPS) EM_MIPS #elif defined(Q_PROCESSOR_POWER_32) @@ -128,10 +140,8 @@ struct ElfMachineCheck #elif defined(Q_PROCESSOR_SH) EM_SH #elif defined(Q_PROCESSOR_SPARC_V9) -# warning "Please confirm that this is correct for Linux and Solaris" EM_SPARCV9 #elif defined(Q_PROCESSOR_SPARC_64) -# warning "Please confirm that this is correct for Linux and Solaris" EM_SPARCV9 #elif defined(Q_PROCESSOR_SPARC) EM_SPARC @@ -329,7 +339,7 @@ Q_DECL_UNUSED Q_DECL_COLD_FUNCTION static QDebug &operator<<(QDebug &d, ElfHeade case ELFOSABI_SYSV: d << " (SYSV"; break; case ELFOSABI_HPUX: d << " (HP-UX"; break; case ELFOSABI_NETBSD: d << " (NetBSD"; break; - case ELFOSABI_GNU: d << " (GNU/Linux"; break; + case ELFOSABI_LINUX: d << " (GNU/Linux"; break; case ELFOSABI_SOLARIS: d << " (Solaris"; break; case ELFOSABI_AIX: d << " (AIX"; break; case ELFOSABI_IRIX: d << " (IRIX"; break; @@ -373,19 +383,27 @@ Q_DECL_UNUSED Q_DECL_COLD_FUNCTION static QDebug &operator<<(QDebug &d, ElfHeade switch (r.machine) { // list definitely not exhaustive! case EM_NONE: d << ", no machine"; break; + case EM_ALPHA: d << ", Alpha"; break; + case EM_68K: d << ", MC68000"; break; case EM_ARM: d << ", ARM"; break; case EM_AARCH64: d << ", AArch64"; break; #ifdef EM_BLACKFIN case EM_BLACKFIN: d << ", Blackfin"; break; #endif case EM_IA_64: d << ", IA-64"; break; +#ifdef EM_LOONGARCH + case EM_LOONGARCH: d << ", LoongArch"; break; +#endif case EM_MIPS: d << ", MIPS"; break; + case EM_PARISC: d << ", HPPA"; break; case EM_PPC: d << ", PowerPC"; break; case EM_PPC64: d << ", PowerPC 64-bit"; break; #ifdef EM_RISCV case EM_RISCV: d << ", RISC-V"; break; #endif +#ifdef EM_S390 case EM_S390: d << ", S/390"; break; +#endif case EM_SH: d << ", SuperH"; break; case EM_SPARC: d << ", SPARC"; break; case EM_SPARCV9: d << ", SPARCv9"; break; @@ -525,6 +543,8 @@ static bool preScanProgramHeaders(QByteArrayView data, const ErrorMaker &error) // first, validate the extent of the full program header table T::Word e_phnum = header->e_phnum; + if (e_phnum == PN_XNUM) + return error(QLibrary::tr("unimplemented: PN_XNUM program headers")), false; T::Off offset = e_phnum * sizeof(T::Phdr); // can't overflow due to size of T::Half if (qAddOverflow(offset, header->e_phoff, &offset) || offset > size_t(data.size())) return error(QLibrary::tr("program header table extends past the end of the file")), false; @@ -679,7 +699,7 @@ static QLibraryScanResult scanSections(QByteArrayView data, const ErrorMaker &er // sections aren't allowed to extend past the end of the file, unless // they are NOBITS sections if (shdr->sh_type == SHT_NOBITS) - continue;; + continue; if (T::Off end; qAddOverflow(shdr->sh_offset, shdr->sh_size, &end) || end > size_t(data.size())) { return error(QLibrary::tr("section contents extend past the end of the file")); diff --git a/src/corelib/plugin/qfactoryinterface.h b/src/corelib/plugin/qfactoryinterface.h index f95c8ce330..098b7d4201 100644 --- a/src/corelib/plugin/qfactoryinterface.h +++ b/src/corelib/plugin/qfactoryinterface.h @@ -15,7 +15,7 @@ struct Q_CORE_EXPORT QFactoryInterface virtual QStringList keys() const = 0; }; -#ifndef Q_CLANG_QDOC +#ifndef Q_QDOC Q_DECLARE_INTERFACE(QFactoryInterface, "org.qt-project.Qt.QFactoryInterface") #endif diff --git a/src/corelib/plugin/qfactoryloader.cpp b/src/corelib/plugin/qfactoryloader.cpp index 06015127d6..6486414d8c 100644 --- a/src/corelib/plugin/qfactoryloader.cpp +++ b/src/corelib/plugin/qfactoryloader.cpp @@ -5,22 +5,19 @@ #include "qfactoryloader_p.h" #ifndef QT_NO_QOBJECT -#include "qfactoryinterface.h" - #include "private/qcoreapplication_p.h" #include "private/qduplicatetracker_p.h" #include "private/qloggingregistry_p.h" #include "private/qobject_p.h" #include "qcborarray.h" #include "qcbormap.h" +#include "qcborstreamreader.h" #include "qcborvalue.h" -#include "qcborvalue.h" -#include "qdiriterator.h" +#include "qdirlisting.h" #include "qfileinfo.h" #include "qjsonarray.h" #include "qjsondocument.h" #include "qjsonobject.h" -#include "qmap.h" #include "qmutex.h" #include "qplugin.h" #include "qplugin_p.h" @@ -32,30 +29,189 @@ #include <qtcore_tracepoints_p.h> +#include <map> +#include <vector> + QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -bool QPluginParsedMetaData::parse(QByteArrayView raw) +Q_TRACE_POINT(qtcore, QFactoryLoader_update, const QString &fileName); + +namespace { +struct IterationResult +{ + enum Result { + FinishedSearch = 0, + ContinueSearch, + + // parse errors + ParsingError = -1, + InvalidMetaDataVersion = -2, + InvalidTopLevelItem = -3, + InvalidHeaderItem = -4, + }; + Result result; + QCborError error = { QCborError::NoError }; + + Q_IMPLICIT IterationResult(Result r) : result(r) {} + Q_IMPLICIT IterationResult(QCborError e) : result(ParsingError), error(e) {} +}; + +struct QFactoryLoaderIidSearch +{ + QLatin1StringView iid; + bool matchesIid = false; + QFactoryLoaderIidSearch(QLatin1StringView iid) : iid(iid) + { Q_ASSERT(!iid.isEmpty()); } + + static IterationResult::Result skip(QCborStreamReader &reader) + { + // skip this, whatever it is + reader.next(); + return IterationResult::ContinueSearch; + } + + IterationResult::Result operator()(QtPluginMetaDataKeys key, QCborStreamReader &reader) + { + if (key != QtPluginMetaDataKeys::IID) + return skip(reader); + matchesIid = (reader.readAllString() == iid); + return IterationResult::FinishedSearch; + } + IterationResult::Result operator()(QUtf8StringView, QCborStreamReader &reader) + { + return skip(reader); + } +}; + +struct QFactoryLoaderMetaDataKeysExtractor : QFactoryLoaderIidSearch +{ + QCborArray keys; + QFactoryLoaderMetaDataKeysExtractor(QLatin1StringView iid) + : QFactoryLoaderIidSearch(iid) + {} + + IterationResult::Result operator()(QtPluginMetaDataKeys key, QCborStreamReader &reader) + { + if (key == QtPluginMetaDataKeys::IID) { + QFactoryLoaderIidSearch::operator()(key, reader); + return IterationResult::ContinueSearch; + } + if (key != QtPluginMetaDataKeys::MetaData) + return skip(reader); + + if (!matchesIid) + return IterationResult::FinishedSearch; + if (!reader.isMap() || !reader.isLengthKnown()) + return IterationResult::InvalidHeaderItem; + if (!reader.enterContainer()) + return IterationResult::ParsingError; + while (reader.isValid()) { + // the metadata is JSON, so keys are all strings + QByteArray key = reader.readAllUtf8String(); + if (key == "Keys") { + if (!reader.isArray() || !reader.isLengthKnown()) + return IterationResult::InvalidHeaderItem; + keys = QCborValue::fromCbor(reader).toArray(); + break; + } + skip(reader); + } + // warning: we may not have finished iterating over the header + return IterationResult::FinishedSearch; + } + using QFactoryLoaderIidSearch::operator(); +}; +} // unnamed namespace + +template <typename F> static IterationResult iterateInPluginMetaData(QByteArrayView raw, F &&f) { QPluginMetaData::Header header; Q_ASSERT(raw.size() >= qsizetype(sizeof(header))); memcpy(&header, raw.data(), sizeof(header)); if (Q_UNLIKELY(header.version > QPluginMetaData::CurrentMetaDataVersion)) - return setError(QFactoryLoader::tr("Invalid metadata version")); + return IterationResult::InvalidMetaDataVersion; // use fromRawData to keep QCborStreamReader from copying raw = raw.sliced(sizeof(header)); QByteArray ba = QByteArray::fromRawData(raw.data(), raw.size()); - QCborParserError err; - QCborValue metadata = QCborValue::fromCbor(ba, &err); + QCborStreamReader reader(ba); + if (reader.isInvalid()) + return reader.lastError(); + if (!reader.isMap()) + return IterationResult::InvalidTopLevelItem; + if (!reader.enterContainer()) + return reader.lastError(); + while (reader.isValid()) { + IterationResult::Result r; + if (reader.isInteger()) { + // integer key, one of ours + qint64 value = reader.toInteger(); + auto key = QtPluginMetaDataKeys(value); + if (qint64(key) != value) + return IterationResult::InvalidHeaderItem; + if (!reader.next()) + return reader.lastError(); + r = f(key, reader); + } else if (reader.isString()) { + QByteArray key = reader.readAllUtf8String(); + if (key.isNull()) + return reader.lastError(); + r = f(QUtf8StringView(key), reader); + } else { + return IterationResult::InvalidTopLevelItem; + } - if (err.error != QCborError::NoError) - return setError(QFactoryLoader::tr("Metadata parsing error: %1").arg(err.error.toString())); - if (!metadata.isMap()) + if (QCborError e = reader.lastError()) + return e; + if (r != IterationResult::ContinueSearch) + return r; + } + + if (!reader.leaveContainer()) + return reader.lastError(); + return IterationResult::FinishedSearch; +} + +static bool isIidMatch(QByteArrayView raw, QLatin1StringView iid) +{ + QFactoryLoaderIidSearch search(iid); + iterateInPluginMetaData(raw, search); + return search.matchesIid; +} + +bool QPluginParsedMetaData::parse(QByteArrayView raw) +{ + QCborMap map; + auto r = iterateInPluginMetaData(raw, [&](const auto &key, QCborStreamReader &reader) { + QCborValue item = QCborValue::fromCbor(reader); + if (item.isInvalid()) + return IterationResult::ParsingError; + if constexpr (std::is_enum_v<std::decay_t<decltype(key)>>) + map[int(key)] = item; + else + map[QString::fromUtf8(key)] = item; + return IterationResult::ContinueSearch; + }); + + switch (r.result) { + case IterationResult::FinishedSearch: + case IterationResult::ContinueSearch: + break; + + // parse errors + case IterationResult::ParsingError: + return setError(QFactoryLoader::tr("Metadata parsing error: %1").arg(r.error.toString())); + case IterationResult::InvalidMetaDataVersion: + return setError(QFactoryLoader::tr("Invalid metadata version")); + case IterationResult::InvalidTopLevelItem: + case IterationResult::InvalidHeaderItem: return setError(QFactoryLoader::tr("Unexpected metadata contents")); - QCborMap map = metadata.toMap(); - metadata = {}; + } + + // header was validated + auto header = qFromUnaligned<QPluginMetaData::Header>(raw.data()); DecodedArchRequirements archReq = header.version == 0 ? decodeVersion0ArchRequirements(header.plugin_arch_requirements) @@ -96,6 +252,7 @@ QJsonObject QPluginParsedMetaData::toJson() const class QFactoryLoaderPrivate : public QObjectPrivate { Q_DECLARE_PUBLIC(QFactoryLoader) + Q_DISABLE_COPY_MOVE(QFactoryLoaderPrivate) public: QFactoryLoaderPrivate() { } QByteArray iid; @@ -103,8 +260,8 @@ public: ~QFactoryLoaderPrivate(); mutable QMutex mutex; QDuplicateTracker<QString> loadedPaths; - QList<QLibraryPrivate*> libraryList; - QMap<QString,QLibraryPrivate*> keyMap; + std::vector<QLibraryPrivate::UniquePtr> libraries; + std::map<QString, QLibraryPrivate*> keyMap; QString suffix; QString extraSearchPath; Qt::CaseSensitivity cs; @@ -115,7 +272,7 @@ public: #if QT_CONFIG(library) -static Q_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(lcFactoryLoader, "QT_DEBUG_PLUGINS", +Q_STATIC_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(lcFactoryLoader, "QT_DEBUG_PLUGINS", "qt.core.plugin.factoryloader") namespace { @@ -131,10 +288,7 @@ struct QFactoryLoaderGlobals Q_GLOBAL_STATIC(QFactoryLoaderGlobals, qt_factoryloader_global) QFactoryLoaderPrivate::~QFactoryLoaderPrivate() -{ - for (QLibraryPrivate *library : qAsConst(libraryList)) - library->release(); -} + = default; inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path) { @@ -149,7 +303,7 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path) qCDebug(lcFactoryLoader) << "checking directory path" << path << "..."; - QDirIterator plugins(path, + QDirListing plugins(path, #if defined(Q_OS_WIN) QStringList(QStringLiteral("*.dll")), #elif defined(Q_OS_ANDROID) @@ -157,9 +311,9 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path) #endif QDir::Files); - while (plugins.hasNext()) { - QString fileName = plugins.next(); -#ifdef Q_OS_MAC + for (const auto &dirEntry : plugins) { + const QString &fileName = dirEntry.fileName(); +#ifdef Q_OS_DARWIN const bool isDebugPlugin = fileName.endsWith("_debug.dylib"_L1); const bool isDebugLibrary = #ifdef QT_DEBUG @@ -182,8 +336,8 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path) Q_TRACE(QFactoryLoader_update, fileName); - std::unique_ptr<QLibraryPrivate, LibraryReleaser> library; - library.reset(QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath())); + QLibraryPrivate::UniquePtr library; + library.reset(QLibraryPrivate::findOrCreate(dirEntry.canonicalFilePath())); if (!library->isPlugin()) { qCDebug(lcFactoryLoader) << library->errorString << Qt::endl << " not a plugin"; @@ -214,20 +368,20 @@ inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path) // whereas the new one has a Qt version that fits // better constexpr int QtVersionNoPatch = QT_VERSION_CHECK(QT_VERSION_MAJOR, QT_VERSION_MINOR, 0); - QLibraryPrivate *previous = keyMap.value(key); + QLibraryPrivate *&previous = keyMap[key]; int prev_qt_version = 0; if (previous) prev_qt_version = int(previous->metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger()); int qt_version = int(library->metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger()); if (!previous || (prev_qt_version > QtVersionNoPatch && qt_version <= QtVersionNoPatch)) { - keyMap[key] = library.get(); // we WILL .release() + previous = library.get(); // we WILL .release() ++keyUsageCount; } } if (keyUsageCount || keys.isEmpty()) { library->setLoadHints(QLibrary::PreventUnloadHint); // once loaded, don't unload QMutexLocker locker(&mutex); - libraryList += library.release(); + libraries.push_back(std::move(library)); } }; } @@ -264,11 +418,14 @@ QFactoryLoader::~QFactoryLoader() } } -#if defined(Q_OS_UNIX) && !defined (Q_OS_MAC) +#if defined(Q_OS_UNIX) && !defined (Q_OS_DARWIN) QLibraryPrivate *QFactoryLoader::library(const QString &key) const { Q_D(const QFactoryLoader); - return d->keyMap.value(d->cs ? key : key.toLower()); + const auto it = d->keyMap.find(d->cs ? key : key.toLower()); + if (it == d->keyMap.cend()) + return nullptr; + return it->second; } #endif @@ -326,7 +483,7 @@ void QFactoryLoader::setExtraSearchPath(const QString &path) } else { // must re-scan everything d->loadedPaths.clear(); - d->libraryList.clear(); + d->libraries.clear(); d->keyMap.clear(); update(); } @@ -341,7 +498,7 @@ QFactoryLoader::MetaDataList QFactoryLoader::metaData() const QList<QPluginParsedMetaData> metaData; #if QT_CONFIG(library) QMutexLocker locker(&d->mutex); - for (QLibraryPrivate *library : std::as_const(d->libraryList)) + for (const auto &library : d->libraries) metaData.append(library->metaData); #endif @@ -354,6 +511,37 @@ QFactoryLoader::MetaDataList QFactoryLoader::metaData() const continue; metaData.append(std::move(parsed)); } + + // other portions of the code will cast to int (e.g., keyMap()) + Q_ASSERT(metaData.size() <= std::numeric_limits<int>::max()); + return metaData; +} + +QList<QCborArray> QFactoryLoader::metaDataKeys() const +{ + Q_D(const QFactoryLoader); + QList<QCborArray> metaData; +#if QT_CONFIG(library) + QMutexLocker locker(&d->mutex); + for (const auto &library : d->libraries) { + const QCborValue md = library->metaData.value(QtPluginMetaDataKeys::MetaData); + metaData.append(md["Keys"_L1].toArray()); + } +#endif + + QLatin1StringView iid(d->iid.constData(), d->iid.size()); + const auto staticPlugins = QPluginLoader::staticPlugins(); + for (const QStaticPlugin &plugin : staticPlugins) { + QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), + plugin.rawMetaDataSize); + QFactoryLoaderMetaDataKeysExtractor extractor{ iid }; + iterateInPluginMetaData(pluginData, extractor); + if (extractor.matchesIid) + metaData += std::move(extractor.keys); + } + + // other portions of the code will cast to int (e.g., keyMap()) + Q_ASSERT(metaData.size() <= std::numeric_limits<int>::max()); return metaData; } @@ -365,8 +553,8 @@ QObject *QFactoryLoader::instance(int index) const #if QT_CONFIG(library) QMutexLocker lock(&d->mutex); - if (index < d->libraryList.size()) { - QLibraryPrivate *library = d->libraryList.at(index); + if (size_t(index) < d->libraries.size()) { + QLibraryPrivate *library = d->libraries[index].get(); if (QObject *obj = library->pluginInstance()) { if (!obj->parent()) obj->moveToThread(QCoreApplicationPrivate::mainThread()); @@ -374,7 +562,8 @@ QObject *QFactoryLoader::instance(int index) const } return nullptr; } - index -= d->libraryList.size(); + // we know d->libraries.size() <= index <= numeric_limits<decltype(index)>::max() → no overflow + index -= static_cast<int>(d->libraries.size()); lock.unlock(); #endif @@ -382,8 +571,7 @@ QObject *QFactoryLoader::instance(int index) const const QList<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins(); for (QStaticPlugin plugin : staticPlugins) { QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), plugin.rawMetaDataSize); - QPluginParsedMetaData parsed(pluginData); - if (parsed.isError() || parsed.value(QtPluginMetaDataKeys::IID) != iid) + if (!isIidMatch(pluginData, iid)) continue; if (index == 0) @@ -397,10 +585,9 @@ QObject *QFactoryLoader::instance(int index) const QMultiMap<int, QString> QFactoryLoader::keyMap() const { QMultiMap<int, QString> result; - const QList<QPluginParsedMetaData> metaDataList = metaData(); - for (int i = 0; i < metaDataList.size(); ++i) { - const QCborMap metaData = metaDataList.at(i).value(QtPluginMetaDataKeys::MetaData).toMap(); - const QCborArray keys = metaData.value("Keys"_L1).toArray(); + const QList<QCborArray> metaDataList = metaDataKeys(); + for (int i = 0; i < int(metaDataList.size()); ++i) { + const QCborArray &keys = metaDataList[i]; for (QCborValueConstRef key : keys) result.insert(i, key.toString()); } @@ -409,10 +596,9 @@ QMultiMap<int, QString> QFactoryLoader::keyMap() const int QFactoryLoader::indexOf(const QString &needle) const { - const QList<QPluginParsedMetaData> metaDataList = metaData(); - for (int i = 0; i < metaDataList.size(); ++i) { - const QCborMap metaData = metaDataList.at(i).value(QtPluginMetaDataKeys::MetaData).toMap(); - const QCborArray keys = metaData.value("Keys"_L1).toArray(); + const QList<QCborArray> metaDataList = metaDataKeys(); + for (int i = 0; i < int(metaDataList.size()); ++i) { + const QCborArray &keys = metaDataList[i]; for (QCborValueConstRef key : keys) { if (key.toString().compare(needle, Qt::CaseInsensitive) == 0) return i; diff --git a/src/corelib/plugin/qfactoryloader_p.h b/src/corelib/plugin/qfactoryloader_p.h index 0c8f977707..56dc7e6ad1 100644 --- a/src/corelib/plugin/qfactoryloader_p.h +++ b/src/corelib/plugin/qfactoryloader_p.h @@ -74,9 +74,9 @@ public: void update(); static void refreshAll(); -#if defined(Q_OS_UNIX) && !defined (Q_OS_MAC) +#if defined(Q_OS_UNIX) && !defined (Q_OS_DARWIN) QLibraryPrivate *library(const QString &key) const; -#endif // Q_OS_UNIX && !Q_OS_MAC +#endif // Q_OS_UNIX && !Q_OS_DARWIN #endif // QT_CONFIG(library) void setExtraSearchPath(const QString &path); @@ -86,6 +86,7 @@ public: using MetaDataList = QList<QPluginParsedMetaData>; MetaDataList metaData() const; + QList<QCborArray> metaDataKeys() const; QObject *instance(int index) const; }; diff --git a/src/corelib/plugin/qlibrary.cpp b/src/corelib/plugin/qlibrary.cpp index 6a3f13bb81..8f1db7e4d4 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?) @@ -51,7 +55,7 @@ static constexpr bool QtBuildIsDebug = true; #endif Q_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(qt_lcDebugPlugins, "QT_DEBUG_PLUGINS", "qt.core.plugin.loader") -static Q_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(lcDebugLibrary, "QT_DEBUG_PLUGINS", "qt.core.library") +Q_STATIC_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(lcDebugLibrary, "QT_DEBUG_PLUGINS", "qt.core.library") /*! \class QLibrary @@ -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. diff --git a/src/corelib/plugin/qlibrary_p.h b/src/corelib/plugin/qlibrary_p.h index 8c722a3283..87d36ee5c8 100644 --- a/src/corelib/plugin/qlibrary_p.h +++ b/src/corelib/plugin/qlibrary_p.h @@ -28,6 +28,8 @@ # include "QtCore/qt_windows.h" #endif +#include <memory> + QT_REQUIRE_CONFIG(library); QT_BEGIN_NAMESPACE @@ -38,6 +40,9 @@ struct QLibraryScanResult { qsizetype pos; qsizetype length; +#if defined(Q_OF_MACH_O) + bool isEncrypted = false; +#endif }; class QLibraryStore; @@ -51,6 +56,12 @@ public: #endif enum UnloadFlag { UnloadSys, NoUnloadSys }; + struct Deleter { + // QLibraryPrivate::release() is not, yet, and cannot easily be made, noexcept: + void operator()(QLibraryPrivate *p) const { p->release(); } + }; + using UniquePtr = std::unique_ptr<QLibraryPrivate, Deleter>; + const QString fileName; const QString fullVersion; diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp index 7ad6e9e335..a6fb5403cd 100644 --- a/src/corelib/plugin/qlibrary_unix.cpp +++ b/src/corelib/plugin/qlibrary_unix.cpp @@ -12,7 +12,7 @@ #include <dlfcn.h> -#ifdef Q_OS_MAC +#ifdef Q_OS_DARWIN # include <private/qcore_mac_p.h> #endif @@ -25,12 +25,6 @@ QT_BEGIN_NAMESPACE using namespace Qt::StringLiterals; -static QString qdlerror() -{ - const char *err = dlerror(); - return err ? u'(' + QString::fromLocal8Bit(err) + u')' : QString(); -} - QStringList QLibraryPrivate::suffixes_sys(const QString &fullVersion) { QStringList suffixes; @@ -72,7 +66,7 @@ QStringList QLibraryPrivate::suffixes_sys(const QString &fullVersion) # endif } #endif -# ifdef Q_OS_MAC +# ifdef Q_OS_DARWIN if (!fullVersion.isEmpty()) { suffixes << ".%1.bundle"_L1.arg(fullVersion); suffixes << ".%1.dylib"_L1.arg(fullVersion); @@ -90,6 +84,11 @@ QStringList QLibraryPrivate::prefixes_sys() bool QLibraryPrivate::load_sys() { +#if defined(Q_OS_WASM) && defined(QT_STATIC) + // emscripten does not support dlopen when using static linking + return false; +#endif + QMutexLocker locker(&mutex); QString attempt; QFileSystemEntry fsEntry(fileName); @@ -164,7 +163,7 @@ bool QLibraryPrivate::load_sys() QStringList tmp; qSwap(tmp, list); list.reserve(tmp.size() * 2); - for (const QString &s : qAsConst(tmp)) { + for (const QString &s : std::as_const(tmp)) { QString modifiedPath = s; f(&modifiedPath); list.append(modifiedPath); @@ -194,7 +193,7 @@ bool QLibraryPrivate::load_sys() continue; if (loadHints & QLibrary::LoadArchiveMemberHint) { attempt = name; - int lparen = attempt.indexOf(u'('); + qsizetype lparen = attempt.indexOf(u'('); if (lparen == -1) lparen = attempt.size(); attempt = path + prefixes.at(prefix) + attempt.insert(lparen, suffixes.at(suffix)); @@ -208,14 +207,6 @@ bool QLibraryPrivate::load_sys() auto attemptFromBundle = attempt; hnd = dlopen(QFile::encodeName(attemptFromBundle.replace(u'/', u'_')), dlFlags); } - if (hnd) { - using JniOnLoadPtr = jint (*)(JavaVM *vm, void *reserved); - JniOnLoadPtr jniOnLoad = reinterpret_cast<JniOnLoadPtr>(dlsym(hnd, "JNI_OnLoad")); - if (jniOnLoad && jniOnLoad(QJniEnvironment::javaVM(), nullptr) == JNI_ERR) { - dlclose(hnd); - hnd = nullptr; - } - } #endif if (!hnd && fileName.startsWith(u'/') && QFile::exists(attempt)) { @@ -228,7 +219,7 @@ bool QLibraryPrivate::load_sys() } } -#ifdef Q_OS_MAC +#ifdef Q_OS_DARWIN if (!hnd) { QByteArray utf8Bundle = fileName.toUtf8(); QCFType<CFURLRef> bundleUrl = CFURLCreateFromFileSystemRepresentation(NULL, reinterpret_cast<const UInt8*>(utf8Bundle.data()), utf8Bundle.length(), true); @@ -245,7 +236,8 @@ bool QLibraryPrivate::load_sys() locker.relock(); if (!hnd) { - errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName, qdlerror()); + errorString = QLibrary::tr("Cannot load library %1: %2").arg(fileName, + QLatin1StringView(dlerror())); } if (hnd) { qualifiedFileName = attempt; @@ -257,36 +249,27 @@ bool QLibraryPrivate::load_sys() bool QLibraryPrivate::unload_sys() { - if (dlclose(pHnd.loadAcquire())) { -#if defined (Q_OS_QNX) // Workaround until fixed in QNX; fixes crash in - char *error = dlerror(); // QtDeclarative auto test "qqmlenginecleanup" for instance + bool doTryUnload = true; +#ifndef RTLD_NODELETE + if (loadHints() & QLibrary::PreventUnloadHint) + doTryUnload = false; +#endif + if (doTryUnload && dlclose(pHnd.loadAcquire())) { + const char *error = dlerror(); +#if defined (Q_OS_QNX) + // Workaround until fixed in QNX; fixes crash in + // QtDeclarative auto test "qqmlenginecleanup" for instance if (!qstrcmp(error, "Shared objects still referenced")) // On QNX that's only "informative" return true; +#endif errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName, QLatin1StringView(error)); -#else - errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName, qdlerror()); -#endif return false; } errorString.clear(); return true; } -#if defined(Q_OS_LINUX) -Q_CORE_EXPORT QFunctionPointer qt_linux_find_symbol_sys(const char *symbol) -{ - return QFunctionPointer(dlsym(RTLD_DEFAULT, symbol)); -} -#endif - -#ifdef Q_OS_MAC -Q_CORE_EXPORT QFunctionPointer qt_mac_resolve_sys(void *handle, const char *symbol) -{ - return QFunctionPointer(dlsym(handle, symbol)); -} -#endif - QFunctionPointer QLibraryPrivate::resolve_sys(const char *symbol) { QFunctionPointer address = QFunctionPointer(dlsym(pHnd.loadAcquire(), symbol)); diff --git a/src/corelib/plugin/qlibrary_win.cpp b/src/corelib/plugin/qlibrary_win.cpp index 741dd8cff9..c95118e554 100644 --- a/src/corelib/plugin/qlibrary_win.cpp +++ b/src/corelib/plugin/qlibrary_win.cpp @@ -59,7 +59,7 @@ bool QLibraryPrivate::load_sys() locker.unlock(); Handle hnd = nullptr; - for (const QString &attempt : qAsConst(attempts)) { + for (const QString &attempt : std::as_const(attempts)) { hnd = LoadLibrary(reinterpret_cast<const wchar_t*>(QDir::toNativeSeparators(attempt).utf16())); // If we have a handle or the last error is something other than "unable diff --git a/src/corelib/plugin/qmachparser.cpp b/src/corelib/plugin/qmachparser.cpp index 979ce2c7de..7a82b84cb3 100644 --- a/src/corelib/plugin/qmachparser.cpp +++ b/src/corelib/plugin/qmachparser.cpp @@ -56,6 +56,23 @@ static QLibraryScanResult notfound(const QString &reason, QString *errorString) return {}; } +static bool isEncrypted(const my_mach_header *header) +{ + auto commandCursor = uintptr_t(header) + sizeof(my_mach_header); + for (uint32_t i = 0; i < header->ncmds; ++i) { + load_command *loadCommand = reinterpret_cast<load_command *>(commandCursor); + if (loadCommand->cmd == LC_ENCRYPTION_INFO || loadCommand->cmd == LC_ENCRYPTION_INFO_64) { + // The layout of encryption_info_command and encryption_info_command_64 is the same + // up until and including cryptid, so we can treat it as encryption_info_command. + auto encryptionInfoCommand = reinterpret_cast<encryption_info_command*>(loadCommand); + return encryptionInfoCommand->cryptid != 0; + } + commandCursor += loadCommand->cmdsize; + } + + return false; +} + QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *errorString) { // The minimum size of a Mach-O binary we're interested in. @@ -166,8 +183,12 @@ QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *e if (sect[j].size < sizeof(QPluginMetaData::MagicHeader)) return notfound(QLibrary::tr(".qtmetadata section is too small"), errorString); + const bool binaryIsEncrypted = isEncrypted(header); qsizetype pos = reinterpret_cast<const char *>(header) - m_s + sect[j].offset; - if (IncludeValidityChecks) { + + // We can not read the section data of encrypted libraries until they + // have been dlopened(), so skip validity check if that's the case. + if (IncludeValidityChecks && !binaryIsEncrypted) { QByteArrayView expectedMagic = QByteArrayView::fromArray(QPluginMetaData::MagicString); QByteArrayView actualMagic = QByteArrayView(m_s + pos, expectedMagic.size()); if (expectedMagic != actualMagic) @@ -175,7 +196,7 @@ QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *e } pos += sizeof(QPluginMetaData::MagicString); - return { pos, qsizetype(sect[j].size - sizeof(QPluginMetaData::MagicString)) }; + return { pos, qsizetype(sect[j].size - sizeof(QPluginMetaData::MagicString)), binaryIsEncrypted }; } } diff --git a/src/corelib/plugin/qplugin.h b/src/corelib/plugin/qplugin.h index 61dff6ab53..909c8acdcc 100644 --- a/src/corelib/plugin/qplugin.h +++ b/src/corelib/plugin/qplugin.h @@ -13,6 +13,8 @@ #include <QtCore/qpointer.h> #include <QtCore/qjsonobject.h> +#include <QtCore/q20algorithm.h> + QT_BEGIN_NAMESPACE // Used up to Qt 6.2 @@ -42,10 +44,8 @@ struct QPluginMetaData template <size_t OSize, typename OO, size_t ISize, typename II> static constexpr void copy(OO (&out)[OSize], II (&in)[ISize]) { - // std::copy is not constexpr until C++20 static_assert(OSize <= ISize, "Output would not be fully initialized"); - for (size_t i = 0; i < OSize; ++i) - out[i] = in[i]; + q20::copy_n(in, OSize, out); } static constexpr quint8 archRequirements() @@ -127,7 +127,7 @@ void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin staticPlugin); #if defined(Q_OF_ELF) || (defined(Q_OS_WIN) && (defined (Q_CC_GNU) || defined(Q_CC_CLANG))) # define QT_PLUGIN_METADATA_SECTION \ __attribute__ ((section (".qtmetadata"))) __attribute__((used)) -#elif defined(Q_OS_MAC) +#elif defined(Q_OS_DARWIN) # define QT_PLUGIN_METADATA_SECTION \ __attribute__ ((section ("__TEXT,qtmetadata"))) __attribute__((used)) #elif defined(Q_CC_MSVC) diff --git a/src/corelib/plugin/qplugin.qdoc b/src/corelib/plugin/qplugin.qdoc index dd4aa5f4b0..0ca248a548 100644 --- a/src/corelib/plugin/qplugin.qdoc +++ b/src/corelib/plugin/qplugin.qdoc @@ -19,13 +19,10 @@ This macro associates the given \a Identifier (a string literal) to the interface class called \a ClassName. The \a Identifier must - be unique. For example: - - \snippet plugandpaint/app/interfaces.h 3 + be unique. This macro is normally used right after the class definition for - \a ClassName, in a header file. See the - \l{tools/plugandpaint/app}{Plug & Paint} example for details. + \a ClassName, in a header file. If you want to use Q_DECLARE_INTERFACE with interface classes declared in a namespace then you have to make sure the Q_DECLARE_INTERFACE @@ -53,8 +50,6 @@ \snippet code/doc_src_qplugin.cpp 1 - See the \l{tools/plugandpaint/app}{Plug & Paint} example for details. - Note that the class this macro appears on must be default-constructible. FILE is optional and points to a json file. diff --git a/src/corelib/plugin/qpluginloader.cpp b/src/corelib/plugin/qpluginloader.cpp index e86d4ccec8..03b8cfbb84 100644 --- a/src/corelib/plugin/qpluginloader.cpp +++ b/src/corelib/plugin/qpluginloader.cpp @@ -71,7 +71,7 @@ using namespace Qt::StringLiterals; link to plugins statically. You can use QLibrary if you need to load dynamic libraries in a statically linked application. - \sa QLibrary, {Plug & Paint Example} + \sa QLibrary */ static constexpr QLibrary::LoadHints defaultLoadHints = QLibrary::PreventUnloadHint; @@ -235,7 +235,7 @@ static QString locatePlugin(const QString& fileName) suffixes.prepend(QString()); // Split up "subdir/filename" - const int slash = fileName.lastIndexOf(u'/'); + const qsizetype slash = fileName.lastIndexOf(u'/'); const auto baseName = QStringView{fileName}.mid(slash + 1); const auto basePath = isAbsolute ? QStringView() : QStringView{fileName}.left(slash + 1); // keep the '/' @@ -246,9 +246,9 @@ static QString locatePlugin(const QString& fileName) paths = QCoreApplication::libraryPaths(); } - for (const QString &path : qAsConst(paths)) { - for (const QString &prefix : qAsConst(prefixes)) { - for (const QString &suffix : qAsConst(suffixes)) { + for (const QString &path : std::as_const(paths)) { + for (const QString &prefix : std::as_const(prefixes)) { + for (const QString &suffix : std::as_const(suffixes)) { #ifdef Q_OS_ANDROID { QString pluginPath = basePath + prefix + baseName + suffix; @@ -349,10 +349,11 @@ QString QPluginLoader::errorString() const void QPluginLoader::setLoadHints(QLibrary::LoadHints loadHints) { if (!d) { - d = QLibraryPrivate::findOrCreate(QString()); // ugly, but we need a d-ptr + d = QLibraryPrivate::findOrCreate({}, {}, loadHints); // ugly, but we need a d-ptr d->errorString.clear(); + } else { + d->setLoadHints(loadHints); } - d->setLoadHints(loadHints); } QLibrary::LoadHints QPluginLoader::loadHints() const @@ -379,7 +380,19 @@ Q_GLOBAL_STATIC(StaticPluginList, staticPluginList) */ void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin plugin) { - staticPluginList()->append(plugin); + // using operator* because we shouldn't be registering plugins while + // unloading the application! + StaticPluginList &plugins = *staticPluginList; + + // insert the plugin in the list, sorted by address, so we can detect + // duplicate registrations + auto comparator = [=](const QStaticPlugin &p1, const QStaticPlugin &p2) { + using Less = std::less<decltype(plugin.instance)>; + return Less{}(p1.instance, p2.instance); + }; + auto pos = std::lower_bound(plugins.constBegin(), plugins.constEnd(), plugin, comparator); + if (pos == plugins.constEnd() || pos->instance != plugin.instance) + plugins.insert(pos, plugin); } /*! @@ -390,12 +403,11 @@ void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin plugin) QObjectList QPluginLoader::staticInstances() { QObjectList instances; - const StaticPluginList *plugins = staticPluginList(); - if (plugins) { - const int numPlugins = plugins->size(); - instances.reserve(numPlugins); - for (int i = 0; i < numPlugins; ++i) - instances += plugins->at(i).instance(); + if (staticPluginList.exists()) { + const StaticPluginList &plugins = *staticPluginList; + instances.reserve(plugins.size()); + for (QStaticPlugin plugin : plugins) + instances += plugin.instance(); } return instances; } diff --git a/src/corelib/plugin/quuid.cpp b/src/corelib/plugin/quuid.cpp index 8613f96c21..b3fad47ee6 100644 --- a/src/corelib/plugin/quuid.cpp +++ b/src/corelib/plugin/quuid.cpp @@ -13,6 +13,9 @@ QT_BEGIN_NAMESPACE +// ensure QList of this is efficient +static_assert(QTypeInfo<QUuid::Id128Bytes>::isRelocatable); + // 16 bytes (a uint, two shorts and a uchar[8]), each represented by two hex // digits; plus four dashes and a pair of enclosing brace: 16*2 + 4 + 2 = 38. enum { MaxStringUuidLength = 38 }; @@ -113,12 +116,12 @@ static QUuid _q_uuidFromHex(const char *src) return QUuid(); } -static QUuid createFromName(const QUuid &ns, const QByteArray &baseData, QCryptographicHash::Algorithm algorithm, int version) +static QUuid createFromName(QUuid ns, QByteArrayView baseData, QCryptographicHash::Algorithm algorithm, int version) noexcept { - QCryptographicHash hash(algorithm); - hash.addData(ns.toRfc4122()); - hash.addData(baseData); - QByteArrayView hashResult = hash.resultView(); + std::byte buffer[20]; + Q_ASSERT(sizeof buffer >= size_t(QCryptographicHash::hashLength(algorithm))); + QByteArrayView hashResult + = QCryptographicHash::hashInto(buffer, {QByteArrayView{ns.toBytes()}, baseData}, algorithm); Q_ASSERT(hashResult.size() >= 16); hashResult.truncate(16); // Sha1 will be too long @@ -139,6 +142,11 @@ static QUuid createFromName(const QUuid &ns, const QByteArray &baseData, QCrypto \reentrant + \compares strong + \compareswith strong GUID + \note Comparison with GUID is Windows-only. + \endcompareswith + Using \e{U}niversally \e{U}nique \e{ID}entifiers (UUID) is a standard way to uniquely identify entities in a distributed computing environment. A UUID is a 16-byte (128-bit) number @@ -287,6 +295,125 @@ static QUuid createFromName(const QUuid &ns, const QByteArray &baseData, QCrypto */ /*! + \class QUuid::Id128Bytes + \inmodule QtCore + \since 6.6 + + This trivial structure is 128 bits (16 bytes) in size and holds the binary + representation of a UUID. Applications can \c{memcpy()} its contents to and + from many other libraries' UUID or GUID structures that take 128-bit + values. +*/ + +/*! + \fn QUuid::Id128Bytes qFromBigEndian(QUuid::Id128Bytes src) + \since 6.6 + \relates QUuid::Id128Bytes + \overload + + Converts \a src from big-endian byte order and returns the struct holding + the binary representation of UUID in host byte order. + + \sa <QtEndian> +*/ + +/*! + \fn QUuid::Id128Bytes qFromLittleEndian(QUuid::Id128Bytes src) + \since 6.6 + \relates QUuid::Id128Bytes + \overload + + Converts \a src from little-endian byte order and returns the struct holding + the binary representation of UUID in host byte order. + + \sa <QtEndian> +*/ + +/*! + \fn QUuid::Id128Bytes qToBigEndian(QUuid::Id128Bytes src) + \since 6.6 + \relates QUuid::Id128Bytes + \overload + + Converts \a src from host byte order and returns the struct holding the + binary representation of UUID in big-endian byte order. + + \sa <QtEndian> +*/ + +/*! + \fn QUuid::Id128Bytes qToLittleEndian(QUuid::Id128Bytes src) + \since 6.6 + \relates QUuid::Id128Bytes + \overload + + Converts \a src from host byte order and returns the struct holding the + binary representation of UUID in little-endian byte order. + + \sa <QtEndian> +*/ + +/*! + \fn QUuid::QUuid(Id128Bytes id128, QSysInfo::Endian order) noexcept + \since 6.6 + + Creates a QUuid based on the integral \a id128 parameter. The input + \a id128 parameter is considered to have byte order \a order. + + \sa fromBytes(), toBytes(), toRfc4122(), toUInt128() +*/ + +/*! + \fn QUuid::fromUInt128(quint128 uuid, QSysInfo::Endian order) noexcept + \since 6.6 + + Creates a QUuid based on the integral \a uuid parameter. The input \a uuid + parameter is considered to have byte order \a order. + + \note This function is only present on platforms that offer a 128-bit + integer type. + + \sa toUInt128(), fromBytes(), toBytes(), toRfc4122() +*/ + +/*! + \fn quint128 QUuid::toUInt128(QSysInfo::Endian order) const noexcept + \since 6.6 + + Returns a 128-bit integer created from this QUuid on the byte order + specified by \a order. The binary content of this function is the same as + toRfc4122() if the order is QSysInfo::BigEndian. See that function for more + details. + + \note This function is only present on platforms that offer a 128-bit + integer type. + + \sa toRfc4122(), fromUInt128(), toBytes(), fromBytes(), QUuid() +*/ + +/*! + \fn QUuid::Id128Bytes QUuid::toBytes(QSysInfo::Endian order) const noexcept + \since 6.6 + + Returns a 128-bit ID created from this QUuid on the byte order specified + by \a order. The binary content of this function is the same as toRfc4122() + if the order is QSysInfo::BigEndian. See that function for more details. + + \sa toRfc4122(), fromBytes(), QUuid() +*/ + +/*! + \fn QUuid QUuid::fromBytes(const void *bytes, QSysInfo::Endian order) noexcept + \since 6.6 + + Reads 128 bits (16 bytes) from \a bytes using byte order \a order and + returns the QUuid corresponding to those bytes. This function does the same + as fromRfc4122() if the byte order \a order is QSysInfo::BigEndian. + + \sa fromRfc4122() +*/ + +/*! \fn QUuid::QUuid(const GUID &guid) Casts a Windows \a guid to a Qt QUuid. @@ -405,11 +532,14 @@ QUuid QUuid::fromString(QAnyStringView text) noexcept /*! \since 5.0 - \fn QUuid QUuid::createUuidV3(const QUuid &ns, const QByteArray &baseData); + \fn QUuid QUuid::createUuidV3(QUuid ns, QByteArrayView baseData); This function returns a new UUID with variant QUuid::DCE and version QUuid::Md5. \a ns is the namespace and \a baseData is the basic data as described by RFC 4122. + \note In Qt versions prior to 6.8, this function took QByteArray, not + QByteArrayView. + \sa variant(), version(), createUuidV5() */ @@ -425,11 +555,14 @@ QUuid QUuid::fromString(QAnyStringView text) noexcept /*! \since 5.0 - \fn QUuid QUuid::createUuidV5(const QUuid &ns, const QByteArray &baseData); + \fn QUuid QUuid::createUuidV5(QUuid ns, QByteArrayView baseData); This function returns a new UUID with variant QUuid::DCE and version QUuid::Sha1. \a ns is the namespace and \a baseData is the basic data as described by RFC 4122. + \note In Qt versions prior to 6.8, this function took QByteArray, not + QByteArrayView. + \sa variant(), version(), createUuidV3() */ @@ -443,13 +576,13 @@ QUuid QUuid::fromString(QAnyStringView text) noexcept \sa variant(), version(), createUuidV3() */ #ifndef QT_BOOTSTRAPPED -QUuid QUuid::createUuidV3(const QUuid &ns, const QByteArray &baseData) +QUuid QUuid::createUuidV3(QUuid ns, QByteArrayView baseData) noexcept { return createFromName(ns, baseData, QCryptographicHash::Md5, 3); } #endif -QUuid QUuid::createUuidV5(const QUuid &ns, const QByteArray &baseData) +QUuid QUuid::createUuidV5(QUuid ns, QByteArrayView baseData) noexcept { return createFromName(ns, baseData, QCryptographicHash::Sha1, 5); } @@ -468,45 +601,26 @@ QUuid QUuid::createUuidV5(const QUuid &ns, const QByteArray &baseData) \since 4.8 - \sa toRfc4122(), QUuid() + \sa toRfc4122(), QUuid(), fromBytes() */ QUuid QUuid::fromRfc4122(QByteArrayView bytes) noexcept { if (bytes.isEmpty() || bytes.size() != 16) return QUuid(); - - uint d1; - ushort d2, d3; - uchar d4[8]; - - const uchar *data = reinterpret_cast<const uchar *>(bytes.data()); - - d1 = qFromBigEndian<quint32>(data); - data += sizeof(quint32); - d2 = qFromBigEndian<quint16>(data); - data += sizeof(quint16); - d3 = qFromBigEndian<quint16>(data); - data += sizeof(quint16); - - for (int i = 0; i < 8; ++i) { - d4[i] = *(data); - data++; - } - - return QUuid(d1, d2, d3, d4[0], d4[1], d4[2], d4[3], d4[4], d4[5], d4[6], d4[7]); + return fromBytes(bytes.data()); } /*! - \fn bool QUuid::operator==(const QUuid &other) const + \fn bool QUuid::operator==(const QUuid &lhs, const QUuid &rhs) - Returns \c true if this QUuid and the \a other QUuid are identical; + Returns \c true if \a lhs QUuid and the \a rhs QUuid are identical; otherwise returns \c false. */ /*! - \fn bool QUuid::operator!=(const QUuid &other) const + \fn bool QUuid::operator!=(const QUuid &lhs, const QUuid &rhs) - Returns \c true if this QUuid and the \a other QUuid are different; + Returns \c true if \a lhs QUuid and the \a rhs QUuid are different; otherwise returns \c false. */ @@ -623,27 +737,16 @@ QByteArray QUuid::toByteArray(QUuid::StringFormat mode) const \endtable + The bytes in the byte array returned by this function contains the same + binary content as toBytes(). + + \sa toBytes() \since 4.8 */ QByteArray QUuid::toRfc4122() const { - // we know how many bytes a UUID has, I hope :) - QByteArray bytes(16, Qt::Uninitialized); - uchar *data = reinterpret_cast<uchar *>(bytes.data()); - - qToBigEndian(data1, data); - data += sizeof(quint32); - qToBigEndian(data2, data); - data += sizeof(quint16); - qToBigEndian(data3, data); - data += sizeof(quint16); - - for (int i = 0; i < 8; ++i) { - *(data) = data4[i]; - data++; - } - - return bytes; + Id128Bytes bytes = toBytes(); + return QByteArrayView(bytes).toByteArray(); } #ifndef QT_NO_DATASTREAM @@ -653,14 +756,19 @@ QByteArray QUuid::toRfc4122() const */ QDataStream &operator<<(QDataStream &s, const QUuid &id) { - QByteArray bytes; + constexpr int NumBytes = sizeof(QUuid); + static_assert(NumBytes == 16, "Change the serialization format when this ever hits"); + char bytes[NumBytes]; if (s.byteOrder() == QDataStream::BigEndian) { - bytes = id.toRfc4122(); + const auto id128 = id.toBytes(); + static_assert(sizeof(id128) == NumBytes); + memcpy(bytes, &id128, NumBytes); } else { - // we know how many bytes a UUID has, I hope :) - bytes = QByteArray(16, Qt::Uninitialized); - uchar *data = reinterpret_cast<uchar *>(bytes.data()); + auto *data = bytes; + // for historical reasons, our little-endian serialization format + // stores each of the UUID fields in little endian, instead of storing + // a little endian Id128 qToLittleEndian(id.data1, data); data += sizeof(quint32); qToLittleEndian(id.data2, data); @@ -674,9 +782,9 @@ QDataStream &operator<<(QDataStream &s, const QUuid &id) } } - if (s.writeRawData(bytes.data(), 16) != 16) { + if (s.writeRawData(bytes, NumBytes) != NumBytes) s.setStatus(QDataStream::WriteFailed); - } + return s; } @@ -801,51 +909,31 @@ QUuid::Version QUuid::version() const noexcept } /*! - \fn bool QUuid::operator<(const QUuid &other) const + \fn bool QUuid::operator<(const QUuid &lhs, const QUuid &rhs) - Returns \c true if this QUuid has the same \l{Variant field} - {variant field} as the \a other QUuid and is lexicographically - \e{before} the \a other QUuid. If the \a other QUuid has a + Returns \c true if \a lhs QUuid has the same \l{Variant field} + {variant field} as the \a rhs QUuid and is lexicographically + \e{before} the \a rhs QUuid. If the \a rhs QUuid has a different variant field, the return value is determined by comparing the two \l{QUuid::Variant} {variants}. \sa variant() */ -bool QUuid::operator<(const QUuid &other) const noexcept -{ - if (variant() != other.variant()) - return variant() < other.variant(); - -#define ISLESS(f1, f2) if (f1!=f2) return (f1<f2); - ISLESS(data1, other.data1); - ISLESS(data2, other.data2); - ISLESS(data3, other.data3); - for (int n = 0; n < 8; n++) { - ISLESS(data4[n], other.data4[n]); - } -#undef ISLESS - return false; -} /*! - \fn bool QUuid::operator>(const QUuid &other) const + \fn bool QUuid::operator>(const QUuid &lhs, const QUuid &rhs) - Returns \c true if this QUuid has the same \l{Variant field} - {variant field} as the \a other QUuid and is lexicographically - \e{after} the \a other QUuid. If the \a other QUuid has a + Returns \c true if \a lhs QUuid has the same \l{Variant field} + {variant field} as the \a rhs QUuid and is lexicographically + \e{after} the \a rhs QUuid. If the \a rhs QUuid has a different variant field, the return value is determined by comparing the two \l{QUuid::Variant} {variants}. \sa variant() */ -bool QUuid::operator>(const QUuid &other) const noexcept -{ - return other < *this; -} /*! - \fn bool operator<=(const QUuid &lhs, const QUuid &rhs) - \relates QUuid + \fn bool QUuid::operator<=(const QUuid &lhs, const QUuid &rhs) \since 5.5 Returns \c true if \a lhs has the same \l{Variant field} @@ -858,8 +946,7 @@ bool QUuid::operator>(const QUuid &other) const noexcept */ /*! - \fn bool operator>=(const QUuid &lhs, const QUuid &rhs) - \relates QUuid + \fn bool QUuid::operator>=(const QUuid &lhs, const QUuid &rhs) \since 5.5 Returns \c true if \a lhs has the same \l{Variant field} @@ -895,7 +982,7 @@ QUuid QUuid::createUuid() return result; } -#else // Q_OS_WIN +#elif !defined(QT_BOOTSTRAPPED) QUuid QUuid::createUuid() { @@ -909,20 +996,20 @@ QUuid QUuid::createUuid() return result; } -#endif // !Q_OS_WIN +#endif // !Q_OS_WIN && !QT_BOOTSTRAPPED /*! - \fn bool QUuid::operator==(const GUID &guid) const + \fn bool QUuid::operator==(const QUuid &lhs, const GUID &rhs) - Returns \c true if this UUID is equal to the Windows GUID \a guid; + Returns \c true if \a lhs UUID is equal to the Windows GUID \a rhs; otherwise returns \c false. */ /*! - \fn bool QUuid::operator!=(const GUID &guid) const + \fn bool QUuid::operator!=(const QUuid &lhs, const GUID &rhs) - Returns \c true if this UUID is not equal to the Windows GUID \a - guid; otherwise returns \c false. + Returns \c true if \a lhs UUID is not equal to the Windows GUID \a rhs; + otherwise returns \c false. */ #ifndef QT_NO_DEBUG_STREAM diff --git a/src/corelib/plugin/quuid.h b/src/corelib/plugin/quuid.h index 376b06c726..591d9b27c4 100644 --- a/src/corelib/plugin/quuid.h +++ b/src/corelib/plugin/quuid.h @@ -4,9 +4,11 @@ #ifndef QUUID_H #define QUUID_H +#include <QtCore/qcompare.h> +#include <QtCore/qendian.h> #include <QtCore/qstring.h> -#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC) +#if defined(Q_OS_WIN) || defined(Q_QDOC) #ifndef GUID_DEFINED #define GUID_DEFINED typedef struct _GUID @@ -19,14 +21,13 @@ typedef struct _GUID #endif #endif -#if defined(Q_OS_DARWIN) || defined(Q_CLANG_QDOC) +#if defined(Q_OS_DARWIN) || defined(Q_QDOC) Q_FORWARD_DECLARE_CF_TYPE(CFUUID); Q_FORWARD_DECLARE_OBJC_CLASS(NSUUID); #endif QT_BEGIN_NAMESPACE - class Q_CORE_EXPORT QUuid { QUuid(Qt::Initialization) {} @@ -55,11 +56,44 @@ public: Id128 = 3 }; + union alignas(16) Id128Bytes { + quint8 data[16]; + quint16 data16[8]; + quint32 data32[4]; + quint64 data64[2]; +#if defined(__SIZEOF_INT128__) +QT_WARNING_PUSH +QT_WARNING_DISABLE_GCC("-Wpedantic") // ISO C++ does not support ‘__int128’ for ‘data128’ + unsigned __int128 data128[1]; +QT_WARNING_POP +#elif defined(QT_SUPPORTS_INT128) +# error "struct QUuid::Id128Bytes should not depend on QT_SUPPORTS_INT128 for ABI reasons." +# error "Adjust the declaration of the `data128` member above so it is always defined if it's " \ + "supported by the current compiler/architecture in any configuration." +#endif + + constexpr explicit operator QByteArrayView() const noexcept + { + return QByteArrayView(data, sizeof(data)); + } + + friend constexpr Id128Bytes qbswap(Id128Bytes b) noexcept + { + // 128-bit byte swap + auto b0 = qbswap(b.data64[0]); + auto b1 = qbswap(b.data64[1]); + b.data64[0] = b1; + b.data64[1] = b0; + return b; + } + }; + constexpr QUuid() noexcept : data1(0), data2(0), data3(0), data4{0,0,0,0,0,0,0,0} {} constexpr QUuid(uint l, ushort w1, ushort w2, uchar b1, uchar b2, uchar b3, uchar b4, uchar b5, uchar b6, uchar b7, uchar b8) noexcept : data1(l), data2(w1), data3(w2), data4{b1, b2, b3, b4, b5, b6, b7, b8} {} + explicit inline QUuid(Id128Bytes id128, QSysInfo::Endian order = QSysInfo::BigEndian) noexcept; explicit QUuid(QAnyStringView string) noexcept : QUuid{fromString(string)} {} @@ -73,35 +107,77 @@ public: #endif QString toString(StringFormat mode = WithBraces) const; QByteArray toByteArray(StringFormat mode = WithBraces) const; + inline Id128Bytes toBytes(QSysInfo::Endian order = QSysInfo::BigEndian) const noexcept; QByteArray toRfc4122() const; + + static inline QUuid fromBytes(const void *bytes, QSysInfo::Endian order = QSysInfo::BigEndian); #if QT_CORE_REMOVED_SINCE(6, 3) static QUuid fromRfc4122(const QByteArray &); #endif static QUuid fromRfc4122(QByteArrayView) noexcept; + bool isNull() const noexcept; - constexpr bool operator==(const QUuid &orig) const noexcept +#ifdef QT_SUPPORTS_INT128 + static constexpr QUuid fromUInt128(quint128 uuid, QSysInfo::Endian order = QSysInfo::BigEndian) noexcept; + constexpr quint128 toUInt128(QSysInfo::Endian order = QSysInfo::BigEndian) const noexcept; +#endif + +private: + friend constexpr bool comparesEqual(const QUuid &lhs, const QUuid &rhs) noexcept { - if (data1 != orig.data1 || data2 != orig.data2 || - data3 != orig.data3) + if (lhs.data1 != rhs.data1 || lhs.data2 != rhs.data2 || lhs.data3 != rhs.data3) return false; - for (uint i = 0; i < 8; i++) - if (data4[i] != orig.data4[i]) + for (uint i = 0; i < 8; i++) { + if (lhs.data4[i] != rhs.data4[i]) return false; + } return true; } + friend Qt::strong_ordering compareThreeWay(const QUuid &lhs, const QUuid &rhs) noexcept + { + if (lhs.variant() != rhs.variant()) + return Qt::compareThreeWay(lhs.variant(), rhs.variant()); + if (lhs.data1 != rhs.data1) + return Qt::compareThreeWay(lhs.data1, rhs.data1); + if (lhs.data2 != rhs.data2) + return Qt::compareThreeWay(lhs.data2, rhs.data2); + if (lhs.data3 != rhs.data3) + return Qt::compareThreeWay(lhs.data3, rhs.data3); + + int c = std::memcmp(lhs.data4, rhs.data4, sizeof(lhs.data4)); + return Qt::compareThreeWay(c, 0); + } + +public: +/* To prevent a meta-type creation ambiguity on Windows, we put comparison + macros under NOT QT_CORE_REMOVED_SINCE(6, 8) part. */ +#if QT_CORE_REMOVED_SINCE(6, 8) + constexpr bool operator==(const QUuid &orig) const noexcept + { + return comparesEqual(*this, orig); + } constexpr bool operator!=(const QUuid &orig) const noexcept { - return !(*this == orig); + return !operator==(orig); } bool operator<(const QUuid &other) const noexcept; bool operator>(const QUuid &other) const noexcept; - -#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC) +#else +private: +#if defined(__cpp_lib_three_way_comparison) && !defined(Q_QDOC) + QT_DECLARE_3WAY_HELPER_STRONG(QUuid, QUuid, /* non-constexpr */, /* no attributes */) +#else + QT_DECLARE_ORDERING_HELPER_STRONG(QUuid, QUuid, /* non-constexpr */, /* no attributes */) +#endif // defined(__cpp_lib_three_way_comparison) && !defined(Q_QDOC) + Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QUuid) +#endif // QT_CORE_REMOVED_SINCE(6, 8) +public: +#if defined(Q_OS_WIN) || defined(Q_QDOC) // On Windows we have a type GUID that is used by the platform API, so we // provide convenience operators to cast from and to this type. constexpr QUuid(const GUID &guid) noexcept @@ -120,38 +196,57 @@ public: GUID guid = { data1, data2, data3, { data4[0], data4[1], data4[2], data4[3], data4[4], data4[5], data4[6], data4[7] } }; return guid; } - +private: + friend constexpr bool comparesEqual(const QUuid &lhs, const GUID &rhs) noexcept + { + return comparesEqual(lhs, QUuid(rhs)); + } +public: +/* To prevent a meta-type creation ambiguity on Windows, we put comparison + macros under NOT QT_CORE_REMOVED_SINCE(6, 8) part. */ +#if QT_CORE_REMOVED_SINCE(6, 8) constexpr bool operator==(const GUID &guid) const noexcept { - return *this == QUuid(guid); + return comparesEqual(*this, QUuid(guid)); } constexpr bool operator!=(const GUID &guid) const noexcept { - return !(*this == guid); + return !operator==(guid); } +#else + Q_DECLARE_EQUALITY_COMPARABLE_LITERAL_TYPE(QUuid, GUID) +#endif // !QT_CORE_REMOVED_SINCE(6, 8) #endif +public: static QUuid createUuid(); -#ifndef QT_BOOTSTRAPPED - static QUuid createUuidV3(const QUuid &ns, const QByteArray &baseData); +#if QT_CORE_REMOVED_SINCE(6, 8) + static QUuid createUuidV3(const QUuid &ns, const QByteArray &baseData) noexcept; + static QUuid createUuidV5(const QUuid &ns, const QByteArray &baseData) noexcept; #endif - static QUuid createUuidV5(const QUuid &ns, const QByteArray &baseData); + static QUuid createUuidV5(QUuid ns, QByteArrayView baseData) noexcept; #ifndef QT_BOOTSTRAPPED + static QUuid createUuidV3(QUuid ns, QByteArrayView baseData) noexcept; +#if !QT_CORE_REMOVED_SINCE(6, 8) + Q_WEAK_OVERLOAD +#endif static inline QUuid createUuidV3(const QUuid &ns, const QString &baseData) { - return QUuid::createUuidV3(ns, baseData.toUtf8()); + return QUuid::createUuidV3(ns, qToByteArrayViewIgnoringNull(baseData.toUtf8())); } #endif - +#if !QT_CORE_REMOVED_SINCE(6, 8) + Q_WEAK_OVERLOAD +#endif static inline QUuid createUuidV5(const QUuid &ns, const QString &baseData) { - return QUuid::createUuidV5(ns, baseData.toUtf8()); + return QUuid::createUuidV5(ns, qToByteArrayViewIgnoringNull(baseData.toUtf8())); } QUuid::Variant variant() const noexcept; QUuid::Version version() const noexcept; -#if defined(Q_OS_DARWIN) || defined(Q_CLANG_QDOC) +#if defined(Q_OS_DARWIN) || defined(Q_QDOC) static QUuid fromCFUUID(CFUUIDRef uuid); CFUUIDRef toCFUUID() const Q_DECL_CF_RETURNS_RETAINED; static QUuid fromNSUUID(const NSUUID *uuid); @@ -177,10 +272,86 @@ Q_CORE_EXPORT QDebug operator<<(QDebug, const QUuid &); Q_CORE_EXPORT size_t qHash(const QUuid &uuid, size_t seed = 0) noexcept; -inline bool operator<=(const QUuid &lhs, const QUuid &rhs) noexcept -{ return !(rhs < lhs); } -inline bool operator>=(const QUuid &lhs, const QUuid &rhs) noexcept -{ return !(lhs < rhs); } +QUuid::QUuid(Id128Bytes uuid, QSysInfo::Endian order) noexcept +{ + if (order == QSysInfo::LittleEndian) + uuid = qbswap(uuid); + data1 = qFromBigEndian<quint32>(&uuid.data[0]); + data2 = qFromBigEndian<quint16>(&uuid.data[4]); + data3 = qFromBigEndian<quint16>(&uuid.data[6]); + memcpy(data4, &uuid.data[8], sizeof(data4)); +} + +QUuid::Id128Bytes QUuid::toBytes(QSysInfo::Endian order) const noexcept +{ + Id128Bytes result = {}; + qToBigEndian(data1, &result.data[0]); + qToBigEndian(data2, &result.data[4]); + qToBigEndian(data3, &result.data[6]); + memcpy(&result.data[8], data4, sizeof(data4)); + if (order == QSysInfo::LittleEndian) + return qbswap(result); + return result; +} + +QUuid QUuid::fromBytes(const void *bytes, QSysInfo::Endian order) +{ + Id128Bytes result = {}; + memcpy(result.data, bytes, sizeof(result)); + return QUuid(result, order); +} + +#ifdef QT_SUPPORTS_INT128 +constexpr QUuid QUuid::fromUInt128(quint128 uuid, QSysInfo::Endian order) noexcept +{ + QUuid result = {}; + if (order == QSysInfo::BigEndian) { + result.data1 = qFromBigEndian<quint32>(int(uuid)); + result.data2 = qFromBigEndian<quint16>(ushort(uuid >> 32)); + result.data3 = qFromBigEndian<quint16>(ushort(uuid >> 48)); + for (int i = 0; i < 8; ++i) + result.data4[i] = uchar(uuid >> (64 + i * 8)); + } else { + result.data1 = qFromLittleEndian<quint32>(uint(uuid >> 96)); + result.data2 = qFromLittleEndian<quint16>(ushort(uuid >> 80)); + result.data3 = qFromLittleEndian<quint16>(ushort(uuid >> 64)); + for (int i = 0; i < 8; ++i) + result.data4[i] = uchar(uuid >> (56 - i * 8)); + } + return result; +} + +constexpr quint128 QUuid::toUInt128(QSysInfo::Endian order) const noexcept +{ + quint128 result = {}; + if (order == QSysInfo::BigEndian) { + for (int i = 0; i < 8; ++i) + result |= quint64(data4[i]) << (i * 8); + result = result << 64; + result |= quint64(qToBigEndian<quint16>(data3)) << 48; + result |= quint64(qToBigEndian<quint16>(data2)) << 32; + result |= qToBigEndian<quint32>(data1); + } else { + result = qToLittleEndian<quint32>(data1); + result = result << 32; + result |= quint64(qToLittleEndian<quint16>(data2)) << 16; + result |= quint64(qToLittleEndian<quint16>(data3)); + result = result << 64; + for (int i = 0; i < 8; ++i) + result |= quint64(data4[i]) << (56 - i * 8); + } + return result; +} +#endif + +#if defined(Q_QDOC) +// provide fake declarations of qXXXEndian() functions, so that qDoc could +// distinguish them from the general template +QUuid::Id128Bytes qFromBigEndian(QUuid::Id128Bytes src); +QUuid::Id128Bytes qFromLittleEndian(QUuid::Id128Bytes src); +QUuid::Id128Bytes qToBigEndian(QUuid::Id128Bytes src); +QUuid::Id128Bytes qToLittleEndian(QUuid::Id128Bytes src); +#endif QT_END_NAMESPACE |