summaryrefslogtreecommitdiffstats
path: root/src/corelib/plugin
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/plugin')
-rw-r--r--src/corelib/plugin/qcoffpeparser.cpp383
-rw-r--r--src/corelib/plugin/qcoffpeparser_p.h34
-rw-r--r--src/corelib/plugin/qelfparser_p.cpp958
-rw-r--r--src/corelib/plugin/qelfparser_p.h73
-rw-r--r--src/corelib/plugin/qfactoryinterface.cpp40
-rw-r--r--src/corelib/plugin/qfactoryinterface.h42
-rw-r--r--src/corelib/plugin/qfactoryloader.cpp678
-rw-r--r--src/corelib/plugin/qfactoryloader_p.h92
-rw-r--r--src/corelib/plugin/qlibrary.cpp544
-rw-r--r--src/corelib/plugin/qlibrary.h42
-rw-r--r--src/corelib/plugin/qlibrary_p.h69
-rw-r--r--src/corelib/plugin/qlibrary_unix.cpp147
-rw-r--r--src/corelib/plugin/qlibrary_win.cpp53
-rw-r--r--src/corelib/plugin/qmachparser.cpp139
-rw-r--r--src/corelib/plugin/qmachparser_p.h48
-rw-r--r--src/corelib/plugin/qplugin.h197
-rw-r--r--src/corelib/plugin/qplugin.qdoc46
-rw-r--r--src/corelib/plugin/qplugin_p.h101
-rw-r--r--src/corelib/plugin/qpluginloader.cpp149
-rw-r--r--src/corelib/plugin/qpluginloader.h40
-rw-r--r--src/corelib/plugin/qsystemlibrary.cpp86
-rw-r--r--src/corelib/plugin/qsystemlibrary_p.h57
-rw-r--r--src/corelib/plugin/quuid.cpp243
-rw-r--r--src/corelib/plugin/quuid.h179
24 files changed, 2675 insertions, 1765 deletions
diff --git a/src/corelib/plugin/qcoffpeparser.cpp b/src/corelib/plugin/qcoffpeparser.cpp
new file mode 100644
index 0000000000..639e402a07
--- /dev/null
+++ b/src/corelib/plugin/qcoffpeparser.cpp
@@ -0,0 +1,383 @@
+// Copyright (C) 2021 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "qcoffpeparser_p.h"
+
+#include <qendian.h>
+#include <qloggingcategory.h>
+#include <qnumeric.h>
+
+#include <optional>
+
+#include <qt_windows.h>
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+// Whether we include some extra validity checks
+// (checks to ensure we don't read out-of-bounds are always included)
+static constexpr bool IncludeValidityChecks = true;
+
+static constexpr inline auto metadataSectionName() noexcept { return ".qtmetadata"_L1; }
+static constexpr QLatin1StringView truncatedSectionName =
+ metadataSectionName().left(sizeof(IMAGE_SECTION_HEADER::Name));
+
+#ifdef QT_BUILD_INTERNAL
+# define QCOFFPEPARSER_DEBUG
+#endif
+#if defined(QCOFFPEPARSER_DEBUG)
+static Q_LOGGING_CATEGORY(lcCoffPeParser, "qt.core.plugin.coffpeparser")
+# define peDebug qCDebug(lcCoffPeParser) << reinterpret_cast<const char16_t *>(error.errMsg->constData()) << ':'
+#else
+# define peDebug if (false) {} else QNoDebug()
+#endif
+
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_CLANG("-Wunused-const-variable")
+
+static const WORD ExpectedMachine =
+#if 0
+ // nothing, just so everything is #elf
+#elif defined(Q_PROCESSOR_ARM_32)
+ IMAGE_FILE_MACHINE_ARMNT
+#elif defined(Q_PROCESSOR_ARM_64)
+ IMAGE_FILE_MACHINE_ARM64
+#elif defined(Q_PROCESSOR_IA64)
+ IMAGE_FILE_MACHINE_IA64
+#elif defined(Q_PROCESSOR_RISCV_32)
+ 0x5032 // IMAGE_FILE_MACHINE_RISCV32
+#elif defined(Q_PROCESSOR_RISCV_64)
+ 0x5064 // IMAGE_FILE_MACHINE_RISCV64
+#elif defined(Q_PROCESSOR_X86_32)
+ IMAGE_FILE_MACHINE_I386
+#elif defined(Q_PROCESSOR_X86_64)
+ IMAGE_FILE_MACHINE_AMD64
+#else
+# error "Unknown Q_PROCESSOR_xxx macro, please update."
+ IMAGE_FILE_MACHINE_UNKNOWN
+#endif
+ ;
+
+static const WORD ExpectedOptionalHeaderSignature =
+ sizeof(void*) == sizeof(quint64) ? IMAGE_NT_OPTIONAL_HDR64_MAGIC :
+ IMAGE_NT_OPTIONAL_HDR32_MAGIC;
+
+namespace {
+struct ErrorMaker
+{
+ QString *errMsg;
+ constexpr ErrorMaker(QString *errMsg) : errMsg(errMsg) {}
+
+ Q_DECL_COLD_FUNCTION QLibraryScanResult operator()(QString &&text) const
+ {
+ *errMsg = QLibrary::tr("'%1' is not a valid Windows DLL (%2)").arg(*errMsg, std::move(text));
+ return {};
+ }
+
+ Q_DECL_COLD_FUNCTION QLibraryScanResult toosmall() const
+ {
+ *errMsg = QLibrary::tr("'%1' is too small").arg(*errMsg);
+ return {};
+ }
+
+ Q_DECL_COLD_FUNCTION QLibraryScanResult notplugin(QString &&explanation) const
+ {
+ *errMsg = QLibrary::tr("'%1' is not a Qt plugin (%2)").arg(*errMsg, explanation);
+ return {};
+ }
+
+ Q_DECL_COLD_FUNCTION QLibraryScanResult notfound() const
+ {
+ return notplugin(QLibrary::tr("metadata not found"));
+ }
+};
+
+struct HeaderDebug { const IMAGE_NT_HEADERS *h; };
+Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, HeaderDebug h)
+{
+ switch (h.h->Signature & 0xffff) {
+ case IMAGE_OS2_SIGNATURE: return d << "NE executable";
+ case IMAGE_VXD_SIGNATURE: return d << "LE executable";
+ default: return d << "Unknown file type";
+ case IMAGE_NT_SIGNATURE: break;
+ }
+
+ // the FileHeader and the starting portion of OptionalHeader are the same
+ // for 32- and 64-bit
+ switch (h.h->OptionalHeader.Magic) {
+ case IMAGE_NT_OPTIONAL_HDR32_MAGIC: d << "COFF PE"; break;
+ case IMAGE_NT_OPTIONAL_HDR64_MAGIC: d << "COFF PE+"; break;
+ default: return d << "Unknown COFF PE type";
+ }
+
+ QDebugStateSaver saver(d);
+ d.nospace() << Qt::hex << Qt::showbase;
+
+ 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_THUMB: d << "Thumb"; break;
+ case IMAGE_FILE_MACHINE_IA64: d << "IA-64"; break;
+ case IMAGE_FILE_MACHINE_MIPS16: d << "MIPS16"; break;
+ case IMAGE_FILE_MACHINE_MIPSFPU: d << "MIPS with FPU"; break;
+ case IMAGE_FILE_MACHINE_MIPSFPU16: d << "MIPS16 with FPU"; break;
+ case IMAGE_FILE_MACHINE_AMD64: d << "x86-64"; break;
+ case 0xaa64: d << "AArch64"; break;
+ default: d << h.h->FileHeader.Machine; break;
+ }
+
+ // this usually prints "executable DLL"
+ if (h.h->FileHeader.Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE)
+ d << " executable";
+ if (h.h->FileHeader.Characteristics & IMAGE_FILE_DLL)
+ d << " DLL";
+ if (h.h->FileHeader.Characteristics & IMAGE_FILE_LARGE_ADDRESS_AWARE)
+ d << " large-address aware";
+
+ d << ", " << Qt::dec << h.h->FileHeader.NumberOfSections << " sections, ";
+ if (h.h->FileHeader.SizeOfOptionalHeader < sizeof(IMAGE_OPTIONAL_HEADER32))
+ return d;
+
+ auto optDebug = [&d](const auto *hdr) {
+ d << "(Windows " << hdr->MajorSubsystemVersion
+ << '.' << hdr->MinorSubsystemVersion;
+ switch (hdr->Subsystem) {
+ case IMAGE_SUBSYSTEM_NATIVE: d << " native)"; break;
+ case IMAGE_SUBSYSTEM_WINDOWS_GUI: d << " GUI)"; break;
+ case IMAGE_SUBSYSTEM_WINDOWS_CUI: d << " CUI)"; break;
+ default: d << " subsystem " << hdr->Subsystem << ')'; break;
+ }
+
+ d.space() << Qt::hex << hdr->SizeOfHeaders << "header bytes,"
+ << Qt::dec << hdr->NumberOfRvaAndSizes << "RVA entries";
+ };
+
+ if (h.h->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
+ optDebug(reinterpret_cast<const IMAGE_OPTIONAL_HEADER64 *>(&h.h->OptionalHeader));
+ else
+ optDebug(reinterpret_cast<const IMAGE_OPTIONAL_HEADER32 *>(&h.h->OptionalHeader));
+ return d;
+}
+
+struct SectionDebug { const IMAGE_SECTION_HEADER *s; };
+Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, SectionDebug s)
+{
+ QDebugStateSaver saver(d);
+ d << Qt::hex << Qt::showbase;
+ d << "contains";
+ if (s.s->Characteristics & IMAGE_SCN_CNT_CODE)
+ d << "CODE";
+ if (s.s->Characteristics & IMAGE_SCN_CNT_INITIALIZED_DATA)
+ d << "DATA";
+ if (s.s->Characteristics & IMAGE_SCN_CNT_UNINITIALIZED_DATA)
+ d << "BSS";
+
+ d << "flags";
+ d.nospace();
+ if (s.s->Characteristics & IMAGE_SCN_MEM_READ)
+ d << 'R';
+ if (s.s->Characteristics & IMAGE_SCN_MEM_WRITE)
+ d << 'W';
+ if (s.s->Characteristics & IMAGE_SCN_MEM_EXECUTE)
+ d << 'X';
+ if (s.s->Characteristics & IMAGE_SCN_MEM_SHARED)
+ d << 'S';
+ if (s.s->Characteristics & IMAGE_SCN_MEM_DISCARDABLE)
+ d << 'D';
+
+ d.space() << "offset" << s.s->PointerToRawData << "size" << s.s->SizeOfRawData;
+ return d;
+}
+} // unnamed namespace
+
+QT_WARNING_POP
+
+const IMAGE_NT_HEADERS *checkNtHeaders(QByteArrayView data, const ErrorMaker &error)
+{
+ if (size_t(data.size()) < qMax(sizeof(IMAGE_DOS_HEADER), sizeof(IMAGE_NT_HEADERS))) {
+ peDebug << "file too small:" << size_t(data.size());
+ return error.toosmall(), nullptr;
+ }
+
+ // check if there's a DOS image header (almost everything does)
+ size_t off = 0;
+ auto dosHeader = reinterpret_cast<const IMAGE_DOS_HEADER *>(data.data());
+ if (dosHeader->e_magic == IMAGE_DOS_SIGNATURE) {
+ off = dosHeader->e_lfanew;
+ // peDebug << "DOS file header redirects to offset" << Qt::hex << Qt::showbase << off;
+ if (size_t end; qAddOverflow<sizeof(IMAGE_NT_HEADERS)>(off, &end)
+ || end > size_t(data.size())) {
+ peDebug << "file too small:" << size_t(data.size());
+ return error.toosmall(), nullptr;
+ }
+ }
+
+ // now check the NT headers
+ auto ntHeader = reinterpret_cast<const IMAGE_NT_HEADERS *>(data.data() + off);
+ peDebug << HeaderDebug{ntHeader};
+ if (ntHeader->Signature != IMAGE_NT_SIGNATURE) // "PE\0\0"
+ return error(QLibrary::tr("invalid signature")), nullptr;
+ if (ntHeader->FileHeader.Machine != ExpectedMachine)
+ return error(QLibrary::tr("file is for a different processor")), nullptr;
+ if (ntHeader->FileHeader.NumberOfSections == 0)
+ return error(QLibrary::tr("file has no sections")), nullptr;
+
+ WORD requiredCharacteristics =
+ IMAGE_FILE_EXECUTABLE_IMAGE | IMAGE_FILE_DLL;
+ if ((ntHeader->FileHeader.Characteristics & requiredCharacteristics) != requiredCharacteristics)
+ return error(QLibrary::tr("wrong characteristics")), nullptr;
+
+ // the optional header is not optional
+ if (ntHeader->OptionalHeader.Magic != ExpectedOptionalHeaderSignature)
+ return error(QLibrary::tr("file is for a different word size")), nullptr;
+ if (ntHeader->OptionalHeader.SizeOfCode == 0)
+ return error.notplugin(QLibrary::tr("file has no code")), nullptr;
+
+ return ntHeader;
+}
+
+static const IMAGE_SECTION_HEADER *
+findSectionTable(QByteArrayView data, const IMAGE_NT_HEADERS *ntHeader, const ErrorMaker &error)
+{
+ // macro IMAGE_FIRST_SECTION can't overflow due to limited range
+ // of type, but adding to the offset from the DOS header could
+ // overflow on 32-bit
+ static_assert(sizeof(ntHeader->FileHeader.SizeOfOptionalHeader) < sizeof(size_t));
+ static_assert(sizeof(ntHeader->FileHeader.NumberOfSections) < sizeof(size_t));
+
+ size_t off = offsetof(IMAGE_NT_HEADERS, OptionalHeader);
+ off += ntHeader->FileHeader.SizeOfOptionalHeader;
+ if (qAddOverflow<size_t>(off, reinterpret_cast<const char *>(ntHeader) - data.data(), &off))
+ return error.toosmall(), nullptr;
+
+ size_t end = ntHeader->FileHeader.NumberOfSections * sizeof(IMAGE_SECTION_HEADER);
+
+ // validate that the file is big enough for all sections we're
+ // supposed to have
+ if (qAddOverflow(end, off, &end) || end > size_t(data.size()))
+ return error.toosmall(), nullptr;
+
+ peDebug << "contains" << ntHeader->FileHeader.NumberOfSections << "sections at offset" << off;
+ return reinterpret_cast<const IMAGE_SECTION_HEADER *>(data.data() + off);
+}
+
+static std::optional<QByteArrayView>
+findStringTable(QByteArrayView data, const IMAGE_NT_HEADERS *ntHeader, const ErrorMaker &error)
+{
+ // first, find the symbol table
+ size_t off = ntHeader->FileHeader.PointerToSymbolTable;
+ if (off == 0)
+ return QByteArrayView();
+
+ // skip the symbol table to find the string table after it
+ constexpr size_t SymbolEntrySize = 18;
+ size_t size = ntHeader->FileHeader.NumberOfSymbols;
+ if (qMulOverflow<SymbolEntrySize>(size, &size)
+ || qAddOverflow(off, size, &off)
+ || qAddOverflow(off, sizeof(DWORD), &off)
+ || off > size_t(data.size()))
+ return error.toosmall(), std::nullopt;
+
+ off -= sizeof(DWORD);
+
+ // we've found the string table, ensure it's big enough
+ size = qFromUnaligned<DWORD>(data.data() + off);
+ if (size_t end; qAddOverflow(off, size, &end) || end > size_t(data.size()))
+ return error.toosmall(), std::nullopt;
+
+ // the conversion to signed is fine because we checked above it wasn't
+ // bigger than data.size()
+ return data.sliced(off, size);
+}
+
+static QLatin1StringView findSectionName(const IMAGE_SECTION_HEADER *section, QByteArrayView stringTable)
+{
+ auto ptr = reinterpret_cast<const char *>(section->Name);
+ qsizetype n = qstrnlen(ptr, sizeof(section->Name));
+ if (ptr[0] == '/' && !stringTable.isEmpty()) {
+ // long section name
+ // Microsoft's link.exe does not use these and will truncate the
+ // section name to fit section->Name. GNU binutils' ld does use long
+ // section names on executable image files by default (can be disabled
+ // using --disable-long-section-names). LLVM's lld does generate a
+ // string table in MinGW mode, but does not use it for our section.
+
+ static_assert(sizeof(section->Name) - 1 < std::numeric_limits<uint>::digits10);
+ bool ok;
+ qsizetype offset = QByteArrayView(ptr + 1, n - 1).toUInt(&ok);
+ if (!ok || offset >= stringTable.size())
+ return {};
+
+ ptr = stringTable.data() + offset;
+ n = qstrnlen(ptr, stringTable.size() - offset);
+ }
+
+ return {ptr, n};
+}
+
+QLibraryScanResult QCoffPeParser::parse(QByteArrayView data, QString *errMsg)
+{
+ ErrorMaker error(errMsg);
+ auto ntHeaders = checkNtHeaders(data, error);
+ if (!ntHeaders)
+ return {};
+
+ auto section = findSectionTable(data, ntHeaders, error);
+ if (!ntHeaders)
+ return {};
+
+ QByteArrayView stringTable;
+ if (auto optional = findStringTable(data, ntHeaders, error))
+ stringTable = *optional;
+ else
+ return {};
+
+ // scan the sections now
+ const auto sectionTableEnd = section + ntHeaders->FileHeader.NumberOfSections;
+ for ( ; section < sectionTableEnd; ++section) {
+ QLatin1StringView sectionName = findSectionName(section, stringTable);
+ peDebug << "section" << sectionName << SectionDebug{section};
+ if (IncludeValidityChecks && sectionName.isEmpty())
+ return error(QLibrary::tr("a section name is empty or extends past the end of the file"));
+
+ size_t offset = section->PointerToRawData;
+ if (size_t end; qAddOverflow<size_t>(offset, section->SizeOfRawData, &end)
+ || end > size_t(data.size()))
+ return error(QLibrary::tr("section contents extend past the end of the file"));
+
+ DWORD type = section->Characteristics
+ & (IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA
+ | IMAGE_SCN_CNT_UNINITIALIZED_DATA);
+ if (type != IMAGE_SCN_CNT_INITIALIZED_DATA)
+ continue;
+
+ // if we do have a string table, the name may be complete
+ if (sectionName != truncatedSectionName && sectionName != metadataSectionName())
+ continue;
+ peDebug << "found .qtmetadata section";
+
+ size_t size = qMin(section->SizeOfRawData, section->Misc.VirtualSize);
+ if (size < sizeof(QPluginMetaData::MagicHeader))
+ return error(QLibrary::tr(".qtmetadata section is too small"));
+ if (IncludeValidityChecks) {
+ QByteArrayView expectedMagic = QByteArrayView::fromArray(QPluginMetaData::MagicString);
+ QByteArrayView actualMagic = data.sliced(offset, expectedMagic.size());
+ if (expectedMagic != actualMagic)
+ return error(QLibrary::tr(".qtmetadata section has incorrect magic"));
+
+ if (section->Characteristics & IMAGE_SCN_MEM_WRITE)
+ return error(QLibrary::tr(".qtmetadata section is writable"));
+ if (section->Characteristics & IMAGE_SCN_MEM_EXECUTE)
+ return error(QLibrary::tr(".qtmetadata section is executable"));
+ }
+
+ return { qsizetype(offset + sizeof(QPluginMetaData::MagicString)),
+ qsizetype(size - sizeof(QPluginMetaData::MagicString)) };
+ }
+
+ return error.notfound();
+}
+
+QT_END_NAMESPACE
diff --git a/src/corelib/plugin/qcoffpeparser_p.h b/src/corelib/plugin/qcoffpeparser_p.h
new file mode 100644
index 0000000000..19c650fee0
--- /dev/null
+++ b/src/corelib/plugin/qcoffpeparser_p.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2021 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+// no, this is not a misspelling of "coffeeparser"
+#ifndef QCOFFPEPARSER_H
+#define QCOFFPEPARSER_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qlibrary_p.h"
+
+#if defined(Q_OS_WIN)
+
+QT_BEGIN_NAMESPACE
+
+struct QCoffPeParser
+{
+ static QLibraryScanResult parse(QByteArrayView data, QString *errMsg);
+};
+
+QT_END_NAMESPACE
+
+#endif // defined(Q_OF_ELF) && defined(Q_CC_GNU)
+
+#endif // QCOFFPEPARSER_H
diff --git a/src/corelib/plugin/qelfparser_p.cpp b/src/corelib/plugin/qelfparser_p.cpp
index bbcfe2f865..7f6271cde4 100644
--- a/src/corelib/plugin/qelfparser_p.cpp
+++ b/src/corelib/plugin/qelfparser_p.cpp
@@ -1,239 +1,789 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2021 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qelfparser_p.h"
-#if defined (Q_OF_ELF) && defined(Q_CC_GNU)
+#ifdef Q_OF_ELF
#include "qlibrary_p.h"
-#include <qdebug.h>
+
+#include <qloggingcategory.h>
+#include <qnumeric.h>
+#include <qsysinfo.h>
+
+#if __has_include(<elf.h>)
+# include <elf.h>
+#elif __has_include(<sys/elf.h>)
+# include <sys/elf.h>
+#else
+# error "Need ELF header to parse plugins."
+#endif
QT_BEGIN_NAMESPACE
-// #define QELFPARSER_DEBUG 1
-
-const char *QElfParser::parseSectionHeader(const char *data, ElfSectionHeader *sh)
-{
- sh->name = qFromUnaligned<qelfword_t>(data);
- data += sizeof(qelfword_t); // sh_name
- sh->type = qFromUnaligned<qelfword_t>(data);
- data += sizeof(qelfword_t) // sh_type
- + sizeof(qelfaddr_t) // sh_flags
- + sizeof(qelfaddr_t); // sh_addr
- sh->offset = qFromUnaligned<qelfoff_t>(data);
- data += sizeof(qelfoff_t); // sh_offset
- sh->size = qFromUnaligned<qelfoff_t>(data);
- data += sizeof(qelfoff_t); // sh_size
- return data;
-}
+using namespace Qt::StringLiterals;
-auto QElfParser::parse(const char *dataStart, ulong fdlen, const QString &library,
- QLibraryPrivate *lib, qsizetype *pos, qsizetype *sectionlen) -> ScanResult
-{
+// ### Qt7: propagate the constant and eliminate dead code
+static constexpr bool ElfNotesAreMandatory = QT_VERSION >= QT_VERSION_CHECK(7,0,0);
+
+// Whether we include some extra validity checks
+// (checks to ensure we don't read out-of-bounds are always included)
+static constexpr bool IncludeValidityChecks = true;
+
+#ifdef QT_BUILD_INTERNAL
+# define QELFPARSER_DEBUG
+#endif
#if defined(QELFPARSER_DEBUG)
- qDebug() << "QElfParser::parse " << library;
+static Q_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()
#endif
- if (fdlen < 64) {
- if (lib)
- lib->errorString = QLibrary::tr("'%1' is not an ELF object (%2)").arg(library, QLibrary::tr("file too small"));
- return NotElf;
- }
- const char *data = dataStart;
- if (qstrncmp(data, "\177ELF", 4) != 0) {
- if (lib)
- lib->errorString = QLibrary::tr("'%1' is not an ELF object").arg(library);
- return NotElf;
- }
- // 32 or 64 bit
- if (data[4] != 1 && data[4] != 2) {
- if (lib)
- lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library, QLibrary::tr("odd cpu architecture"));
- return Corrupt;
- }
-
- /* If you remove this check, to read ELF objects of a different arch, please make sure you modify the typedefs
- to match the _plugin_ architecture.
- */
- constexpr int ExpectedClass = (sizeof(void *) == 4) ? 1 : 2;
- if (data[4] != ExpectedClass) {
- if (lib)
- lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library, QLibrary::tr("wrong cpu architecture"));
- return Corrupt;
- }
-
- // endian
- constexpr int ExpectedEndianness = (Q_BYTE_ORDER == Q_LITTLE_ENDIAN) ? 1 : 2;
- if (data[5] != ExpectedEndianness) {
- if (lib)
- lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library, QLibrary::tr("odd endianness"));
- return Corrupt;
- }
-
- data += 16 // e_ident
- + sizeof(qelfhalf_t) // e_type
- + sizeof(qelfhalf_t) // e_machine
- + sizeof(qelfword_t) // e_version
- + sizeof(qelfaddr_t) // e_entry
- + sizeof(qelfoff_t); // e_phoff
-
- qelfoff_t e_shoff = qFromUnaligned<qelfoff_t> (data);
- data += sizeof(qelfoff_t) // e_shoff
- + sizeof(qelfword_t); // e_flags
-
- qelfhalf_t e_shsize = qFromUnaligned<qelfhalf_t> (data);
-
- if (e_shsize > fdlen) {
- if (lib)
- lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library, QLibrary::tr("unexpected e_shsize"));
- return Corrupt;
- }
-
- data += sizeof(qelfhalf_t) // e_ehsize
- + sizeof(qelfhalf_t) // e_phentsize
- + sizeof(qelfhalf_t); // e_phnum
-
- qelfhalf_t e_shentsize = qFromUnaligned<qelfhalf_t> (data);
-
- if (e_shentsize % 4) {
- if (lib)
- lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library, QLibrary::tr("unexpected e_shentsize"));
- return Corrupt;
- }
- data += sizeof(qelfhalf_t); // e_shentsize
- qelfhalf_t e_shnum = qFromUnaligned<qelfhalf_t> (data);
- data += sizeof(qelfhalf_t); // e_shnum
- qelfhalf_t e_shtrndx = qFromUnaligned<qelfhalf_t> (data);
- data += sizeof(qelfhalf_t); // e_shtrndx
-
- if ((quint32)(e_shnum * e_shentsize) > fdlen) {
- if (lib) {
- const QString message =
- QLibrary::tr("announced %n section(s), each %1 byte(s), exceed file size",
- nullptr, int(e_shnum)).arg(e_shentsize);
- lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)").arg(library, message);
- }
- return Corrupt;
- }
+#ifndef PT_GNU_EH_FRAME
+# define PT_GNU_EH_FRAME 0x6474e550
+#endif
+#ifndef PT_GNU_STACK
+# define PT_GNU_STACK 0x6474e551
+#endif
+#ifndef PT_GNU_RELRO
+# define PT_GNU_RELRO 0x6474e552
+#endif
+#ifndef PT_GNU_PROPERTY
+# define PT_GNU_PROPERTY 0x6474e553
+#endif
-#if defined(QELFPARSER_DEBUG)
- qDebug() << e_shnum << "sections starting at " << ("0x" + QByteArray::number(e_shoff, 16)).data() << "each" << e_shentsize << "bytes";
+#ifndef PN_XNUM
+# define PN_XNUM 0xffff
#endif
- ElfSectionHeader strtab;
- qulonglong soff = e_shoff + qelfword_t(e_shentsize) * qelfword_t(e_shtrndx);
+QT_WARNING_PUSH
+QT_WARNING_DISABLE_CLANG("-Wunused-const-variable")
+
+namespace {
+template <QSysInfo::Endian Order> struct ElfEndianTraits
+{
+ static constexpr unsigned char DataOrder = ELFDATA2LSB;
+ template <typename T> static T fromEndian(T value) { return qFromLittleEndian(value); }
+};
+template <> struct ElfEndianTraits<QSysInfo::BigEndian>
+{
+ static constexpr unsigned char DataOrder = ELFDATA2MSB;
+ template <typename T> static T fromEndian(T value) { return qFromBigEndian(value); }
+};
+
+template <typename EquivalentPointerType> struct ElfTypeTraits
+{
+ static constexpr unsigned char Class = ELFCLASS64;
+
+ // integer types
+ using Half = Elf64_Half;
+ using Word = Elf64_Word;
+ using Addr = Elf64_Addr;
+ using Off = Elf64_Off;
- if ((soff + e_shentsize) > fdlen || soff % 4 || soff == 0) {
- if (lib)
- lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)")
- .arg(library, QLibrary::tr("shstrtab section header seems to be at %1")
- .arg(QString::number(soff, 16)));
- return Corrupt;
+ // structure types
+ using Ehdr = Elf64_Ehdr;
+ using Shdr = Elf64_Shdr;
+ using Phdr = Elf64_Phdr;
+ using Nhdr = Elf64_Nhdr;
+};
+template <> struct ElfTypeTraits<quint32>
+{
+ static constexpr unsigned char Class = ELFCLASS32;
+
+ // integer types
+ using Half = Elf32_Half;
+ using Word = Elf32_Word;
+ using Addr = Elf32_Addr;
+ using Off = Elf32_Off;
+
+ // structure types
+ using Ehdr = Elf32_Ehdr;
+ using Shdr = Elf32_Shdr;
+ using Phdr = Elf32_Phdr;
+ using Nhdr = Elf32_Nhdr;
+};
+
+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)
+ EM_PPC
+#elif defined(Q_PROCESSOR_POWER_64)
+ EM_PPC64
+#elif defined(Q_PROCESSOR_RISCV)
+ EM_RISCV
+#elif defined(Q_PROCESSOR_S390)
+ EM_S390
+#elif defined(Q_PROCESSOR_SH)
+ EM_SH
+#elif defined(Q_PROCESSOR_SPARC_V9)
+ EM_SPARCV9
+#elif defined(Q_PROCESSOR_SPARC_64)
+ EM_SPARCV9
+#elif defined(Q_PROCESSOR_SPARC)
+ EM_SPARC
+#elif defined(Q_PROCESSOR_WASM)
+#elif defined(Q_PROCESSOR_X86_32)
+ EM_386
+#elif defined(Q_PROCESSOR_X86_64)
+ EM_X86_64
+#else
+# error "Unknown Q_PROCESSOR_xxx macro, please update."
+ EM_NONE
+#endif
+ ;
+};
+
+struct ElfHeaderCommonCheck
+{
+ static_assert(std::is_same_v<decltype(Elf32_Ehdr::e_ident), decltype(Elf64_Ehdr::e_ident)>,
+ "e_ident field is not the same in both Elf32_Ehdr and Elf64_Ehdr");
+
+ // bytes 0-3
+ static bool checkElfMagic(const uchar *ident)
+ {
+ return memcmp(ident, ELFMAG, SELFMAG) == 0;
+ }
+
+ // byte 6
+ static bool checkElfVersion(const uchar *ident)
+ {
+ uchar elfversion = ident[EI_VERSION];
+ return elfversion == EV_CURRENT;
}
- parseSectionHeader(dataStart + soff, &strtab);
- m_stringTableFileOffset = strtab.offset;
+ struct CommonHeader {
+ Elf32_Half type;
+ Elf32_Half machine;
+ Elf32_Word version;
+ };
+};
+
+template <typename EquivalentPointerType = quintptr, QSysInfo::Endian Order = QSysInfo::ByteOrder>
+struct ElfHeaderCheck : public ElfHeaderCommonCheck
+{
+ using TypeTraits = ElfTypeTraits<EquivalentPointerType>;
+ using EndianTraits = ElfEndianTraits<Order>;
+ using Ehdr = typename TypeTraits::Ehdr;
- if ((quint32)(strtab.offset + strtab.size) > fdlen || strtab.offset == 0) {
- if (lib)
- lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)")
- .arg(library, QLibrary::tr("string table seems to be at %1")
- .arg(QString::number(strtab.offset, 16)));
- return Corrupt;
+ // byte 4
+ static bool checkClass(const uchar *ident)
+ {
+ uchar klass = ident[EI_CLASS];
+ return klass == TypeTraits::Class;
}
-#if defined(QELFPARSER_DEBUG)
- qDebug(".shstrtab at 0x%s", QByteArray::number(m_stringTableFileOffset, 16).data());
+ // byte 5
+ static bool checkDataOrder(const uchar *ident)
+ {
+ uchar data = ident[EI_DATA];
+ return data == EndianTraits::DataOrder;
+ }
+
+ // byte 7
+ static bool checkOsAbi(const uchar *ident)
+ {
+ uchar osabi = ident[EI_OSABI];
+ // we don't check
+ Q_UNUSED(osabi);
+ return true;
+ }
+
+ // byte 8
+ static bool checkAbiVersion(const uchar *ident)
+ {
+ uchar abiversion = ident[EI_ABIVERSION];
+ // we don't check (and I don't know anyone who uses this)
+ Q_UNUSED(abiversion);
+ return true;
+ }
+
+ // bytes 9-16
+ static bool checkPadding(const uchar *ident)
+ {
+ // why would we check this?
+ Q_UNUSED(ident);
+ return true;
+ }
+
+ static bool checkIdent(const Ehdr &header)
+ {
+ return checkElfMagic(header.e_ident)
+ && checkClass(header.e_ident)
+ && checkDataOrder(header.e_ident)
+ && checkElfVersion(header.e_ident)
+ && checkOsAbi(header.e_ident)
+ && checkAbiVersion(header.e_ident)
+ && checkPadding(header.e_ident);
+ }
+
+ static bool checkType(const Ehdr &header)
+ {
+ return header.e_type == ET_DYN;
+ }
+
+ static bool checkMachine(const Ehdr &header)
+ {
+ return header.e_machine == ElfMachineCheck::ExpectedMachine;
+ }
+
+ static bool checkFileVersion(const Ehdr &header)
+ {
+ return header.e_version == EV_CURRENT;
+ }
+
+ static bool checkHeader(const Ehdr &header)
+ {
+ if (!checkIdent(header))
+ return false;
+ if (!IncludeValidityChecks)
+ return true;
+ return checkType(header)
+ && checkMachine(header)
+ && checkFileVersion(header);
+ }
+
+ Q_DECL_COLD_FUNCTION static QString explainCheckFailure(const Ehdr &header)
+ {
+ if (!checkElfMagic(header.e_ident))
+ return QLibrary::tr("invalid signature");
+ if (!checkClass(header.e_ident))
+ return QLibrary::tr("file is for a different word size");
+ if (!checkDataOrder(header.e_ident))
+ return QLibrary::tr("file is for the wrong endianness");
+ if (!checkElfVersion(header.e_ident) || !checkFileVersion(header))
+ return QLibrary::tr("file has an unknown ELF version");
+ if (!checkOsAbi(header.e_ident) || !checkAbiVersion(header.e_ident))
+ return QLibrary::tr("file has an unexpected ABI");
+ if (!checkType(header))
+ return QLibrary::tr("file is not a shared object");
+ if (!checkMachine(header))
+ return QLibrary::tr("file is for a different processor");
+ return QString();
+ }
+
+ static CommonHeader extractCommonHeader(const uchar *data)
+ {
+ auto header = reinterpret_cast<const Ehdr *>(data);
+ CommonHeader r;
+ r.type = EndianTraits::fromEndian(header->e_type);
+ r.machine = EndianTraits::fromEndian(header->e_machine);
+ r.version = EndianTraits::fromEndian(header->e_version);
+ return r;
+ }
+};
+
+struct ElfHeaderDebug { const uchar *e_ident; };
+Q_DECL_UNUSED Q_DECL_COLD_FUNCTION static QDebug &operator<<(QDebug &d, ElfHeaderDebug h)
+{
+ const uchar *e_ident = h.e_ident;
+ if (!ElfHeaderCommonCheck::checkElfMagic(e_ident)) {
+ d << "Not an ELF file (invalid signature)";
+ return d;
+ }
+
+ QDebugStateSaver saver(d);
+ d.nospace();
+ quint8 elfclass = e_ident[EI_CLASS];
+ switch (elfclass) {
+ case ELFCLASSNONE:
+ default:
+ d << "Invalid ELF file (class " << e_ident[EI_CLASS] << "), ";
+ break;
+ case ELFCLASS32:
+ d << "ELF 32-bit ";
+ break;
+ case ELFCLASS64:
+ d << "ELF 64-bit ";
+ break;
+ }
+
+ quint8 dataorder = e_ident[EI_DATA];
+ switch (dataorder) {
+ case ELFDATANONE:
+ default:
+ d << "invalid endianness (" << e_ident[EI_DATA] << ')';
+ break;
+ case ELFDATA2LSB:
+ d << "LSB";
+ break;
+ case ELFDATA2MSB:
+ d << "MSB";
+ break;
+ }
+
+ switch (e_ident[EI_OSABI]) {
+ case ELFOSABI_SYSV: d << " (SYSV"; break;
+ case ELFOSABI_HPUX: d << " (HP-UX"; break;
+ case ELFOSABI_NETBSD: d << " (NetBSD"; 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;
+ case ELFOSABI_FREEBSD: d << " (FreeBSD"; break;
+ case ELFOSABI_OPENBSD: d << " (OpenBSD"; break;
+ default: d << " (OS ABI " << e_ident[EI_VERSION]; break;
+ }
+
+ if (e_ident[EI_ABIVERSION])
+ d << " v" << e_ident[EI_ABIVERSION];
+ d << ')';
+
+ if (e_ident[EI_VERSION] != 1) {
+ d << ", file version " << e_ident[EI_VERSION];
+ return d;
+ }
+
+ ElfHeaderCommonCheck::CommonHeader r;
+ if (elfclass == ELFCLASS64 && dataorder == ELFDATA2LSB)
+ r = ElfHeaderCheck<quint64, QSysInfo::LittleEndian>::extractCommonHeader(e_ident);
+ else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2LSB)
+ r = ElfHeaderCheck<quint32, QSysInfo::LittleEndian>::extractCommonHeader(e_ident);
+ else if (elfclass == ELFCLASS64 && dataorder == ELFDATA2MSB)
+ r = ElfHeaderCheck<quint64, QSysInfo::BigEndian>::extractCommonHeader(e_ident);
+ else if (elfclass == ELFCLASS32 && dataorder == ELFDATA2MSB)
+ r = ElfHeaderCheck<quint32, QSysInfo::BigEndian>::extractCommonHeader(e_ident);
+ else
+ return d;
+
+ d << ", version " << r.version;
+
+ switch (r.type) {
+ case ET_NONE: d << ", no type"; break;
+ case ET_REL: d << ", relocatable"; break;
+ case ET_EXEC: d << ", executable"; break;
+ case ET_DYN: d << ", shared library or PIC executable"; break;
+ case ET_CORE: d << ", core dump"; break;
+ default: d << ", unknown type " << r.type; break;
+ }
+
+ 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;
+ case EM_386: d << ", i386"; break;
+ case EM_X86_64: d << ", x86-64"; break;
+ default: d << ", other machine type " << r.machine; break;
+ }
- const char *s = dataStart + e_shoff;
- for (int i = 0; i < e_shnum; ++i) {
- ElfSectionHeader sh;
- parseSectionHeader(s, &sh);
- if (sh.name == 0) {
- s += e_shentsize;
- continue;
+ return d;
+}
+
+struct ElfSectionDebug { const ElfHeaderCheck<>::TypeTraits::Shdr *shdr; };
+Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, ElfSectionDebug s)
+{
+ // not exhaustive, just a few common things
+ QDebugStateSaver saver(d);
+ d << Qt::hex << Qt::showbase;
+ d << "type";
+ switch (s.shdr->sh_type) {
+ case SHT_NULL: d << "NULL"; break;
+ case SHT_PROGBITS: d << "PROGBITS"; break;
+ case SHT_SYMTAB: d << "SYMTAB"; break;
+ case SHT_STRTAB: d << "STRTAB"; break;
+ case SHT_RELA: d << "RELA"; break;
+ case SHT_HASH: d << "HASH"; break;
+ case SHT_DYNAMIC: d << "DYNAMIC"; break;
+ case SHT_NOTE: d << "NOTE"; break;
+ case SHT_NOBITS: d << "NOBITS"; break;
+ case SHT_DYNSYM: d << "DYNSYM"; break;
+ case SHT_INIT_ARRAY: d << "INIT_ARRAY"; break;
+ case SHT_FINI_ARRAY: d << "FINI_ARRAY"; break;
+ default: d << s.shdr->sh_type;
+ }
+
+ d << "flags";
+ d.nospace();
+ if (s.shdr->sh_flags & SHF_WRITE)
+ d << 'W';
+ if (s.shdr->sh_flags & SHF_ALLOC)
+ d << 'A';
+ if (s.shdr->sh_flags & SHF_EXECINSTR)
+ d << 'X';
+ if (s.shdr->sh_flags & SHF_STRINGS)
+ d << 'S';
+ if (s.shdr->sh_flags & SHF_TLS)
+ d << 'T';
+
+ d.space() << "offset" << s.shdr->sh_offset << "size" << s.shdr->sh_size;
+ return d;
+}
+
+struct ElfProgramDebug { const ElfHeaderCheck<>::TypeTraits::Phdr *phdr; };
+Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, ElfProgramDebug p)
+{
+ QDebugStateSaver saved(d);
+ d << Qt::hex << Qt::showbase << "program";
+ switch (p.phdr->p_type) {
+ case PT_NULL: d << "NULL"; break;
+ case PT_LOAD: d << "LOAD"; break;
+ case PT_DYNAMIC: d << "DYNAMIC"; break;
+ case PT_INTERP: d << "INTERP"; break;
+ case PT_NOTE: d << "NOTE"; break;
+ case PT_PHDR: d << "PHDR"; break;
+ case PT_TLS: d << "TLS"; break;
+ case PT_GNU_EH_FRAME: d << "GNU_EH_FRAME"; break;
+ case PT_GNU_STACK: d << "GNU_STACK"; break;
+ case PT_GNU_RELRO: d << "GNU_RELRO"; break;
+ case PT_GNU_PROPERTY: d << "GNU_PROPERTY"; break;
+ default: d << "type" << p.phdr->p_type; break;
+ }
+
+ d << "offset" << p.phdr->p_offset
+ << "virtaddr" << p.phdr->p_vaddr
+ << "filesz" << p.phdr->p_filesz
+ << "memsz" << p.phdr->p_memsz
+ << "align" << p.phdr->p_align
+ << "flags";
+
+ d.nospace();
+ if (p.phdr->p_flags & PF_R)
+ d << 'R';
+ if (p.phdr->p_flags & PF_W)
+ d << 'W';
+ if (p.phdr->p_flags & PF_X)
+ d << 'X';
+
+ return d;
+}
+
+struct ErrorMaker
+{
+ QString *errMsg;
+ constexpr ErrorMaker(QString *errMsg) : errMsg(errMsg) {}
+
+
+ Q_DECL_COLD_FUNCTION QLibraryScanResult operator()(QString &&text) const
+ {
+ *errMsg = QLibrary::tr("'%1' is not a valid ELF object (%2)").arg(*errMsg, std::move(text));
+ return {};
+ }
+
+ Q_DECL_COLD_FUNCTION QLibraryScanResult notplugin(QString &&explanation) const
+ {
+ *errMsg = QLibrary::tr("'%1' is not a Qt plugin (%2)").arg(*errMsg, explanation);
+ return {};
+ }
+
+ Q_DECL_COLD_FUNCTION QLibraryScanResult notfound() const
+ {
+ return notplugin(QLibrary::tr("metadata not found"));
+ }
+};
+} // unnamed namespace
+
+QT_WARNING_POP
+
+using T = ElfHeaderCheck<>::TypeTraits;
+
+template <typename F>
+static bool scanProgramHeaders(QByteArrayView data, const ErrorMaker &error, F f)
+{
+ auto header = reinterpret_cast<const T::Ehdr *>(data.data());
+ Q_UNUSED(error);
+
+ auto phdr = reinterpret_cast<const T::Phdr *>(data.data() + header->e_phoff);
+ auto phdr_end = phdr + header->e_phnum;
+ for ( ; phdr != phdr_end; ++phdr) {
+ if (!f(phdr))
+ return false;
+ }
+ return true;
+}
+
+static bool preScanProgramHeaders(QByteArrayView data, const ErrorMaker &error)
+{
+ auto header = reinterpret_cast<const T::Ehdr *>(data.data());
+
+ // 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;
+
+ // confirm validity
+ bool hasCode = false;
+ auto checker = [&](const T::Phdr *phdr) {
+ qEDebug << ElfProgramDebug{phdr};
+
+ if (T::Off end; qAddOverflow(phdr->p_offset, phdr->p_filesz, &end)
+ || end > size_t(data.size()))
+ return error(QLibrary::tr("a program header entry extends past the end of the file")), false;
+
+ // this is not a validity check, it's to exclude debug symbol files
+ if (phdr->p_type == PT_LOAD && phdr->p_filesz != 0 && (phdr->p_flags & PF_X))
+ hasCode = true;
+
+ // this probably applies to all segments, but we'll only apply it to notes
+ if (phdr->p_type == PT_NOTE && qPopulationCount(phdr->p_align) == 1
+ && phdr->p_offset & (phdr->p_align - 1)) {
+ return error(QLibrary::tr("a note segment start is not properly aligned "
+ "(offset 0x%1, alignment %2)")
+ .arg(phdr->p_offset, 6, 16, QChar(u'0'))
+ .arg(phdr->p_align)), false;
}
- const char *shnam = dataStart + m_stringTableFileOffset + sh.name;
-
- if (m_stringTableFileOffset + sh.name > fdlen) {
- if (lib)
- lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)")
- .arg(library, QLibrary::tr("section name %1 of %2 behind end of file")
- .arg(i).arg(e_shnum));
- return Corrupt;
+
+ return true;
+ };
+ if (!scanProgramHeaders(data, error, checker))
+ return false;
+ if (!hasCode)
+ return error.notplugin(QLibrary::tr("file has no code")), false;
+ return true;
+}
+
+static QLibraryScanResult scanProgramHeadersForNotes(QByteArrayView data, const ErrorMaker &error)
+{
+ // minimum metadata payload is 2 bytes
+ constexpr size_t MinPayloadSize = sizeof(QPluginMetaData::Header) + 2;
+ constexpr qptrdiff MinNoteSize = sizeof(QPluginMetaData::ElfNoteHeader) + 2;
+ constexpr size_t NoteNameSize = sizeof(QPluginMetaData::ElfNoteHeader::name);
+ constexpr size_t NoteAlignment = alignof(QPluginMetaData::ElfNoteHeader);
+ constexpr qptrdiff PayloadStartDelta = offsetof(QPluginMetaData::ElfNoteHeader, header);
+ static_assert(MinNoteSize > PayloadStartDelta);
+ static_assert((PayloadStartDelta & (NoteAlignment - 1)) == 0);
+
+ QLibraryScanResult r = {};
+ auto noteFinder = [&](const T::Phdr *phdr) {
+ if (phdr->p_type != PT_NOTE || phdr->p_align != NoteAlignment)
+ return true;
+
+ // check for signed integer overflows, to avoid issues with the
+ // arithmetic below
+ if (qptrdiff(phdr->p_filesz) < 0) {
+ auto h = reinterpret_cast<const T::Ehdr *>(data.data());
+ auto segments = reinterpret_cast<const T::Phdr *>(data.data() + h->e_phoff);
+ qEDebug << "segment" << (phdr - segments) << "contains a note with size"
+ << Qt::hex << Qt::showbase << phdr->p_filesz
+ << "which is larger than half the virtual memory space";
+ return true;
}
-#if defined(QELFPARSER_DEBUG)
- qDebug() << "++++" << i << shnam;
-#endif
+ // iterate over the notes in this segment
+ T::Off offset = phdr->p_offset;
+ const T::Off end_offset = offset + phdr->p_filesz;
+ while (qptrdiff(end_offset - offset) >= MinNoteSize) {
+ auto nhdr = reinterpret_cast<const T::Nhdr *>(data.data() + offset);
+ T::Word n_namesz = nhdr->n_namesz;
+ T::Word n_descsz = nhdr->n_descsz;
+ T::Word n_type = nhdr->n_type;
- if (qstrcmp(shnam, ".qtmetadata") == 0 ) {
- if (!(sh.type & 0x1)) {
- if (shnam[1] == 'r') {
- if (lib)
- lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)")
- .arg(library, QLibrary::tr("empty .rodata. not a library."));
- return Corrupt;
- }
-#if defined(QELFPARSER_DEBUG)
- qDebug()<<"section is not program data. skipped.";
-#endif
- s += e_shentsize;
- continue;
- }
+ // overflow check: calculate where the next note will be, if it exists
+ T::Off next_offset = offset;
+ next_offset += sizeof(T::Nhdr); // can't overflow (we checked above)
+ next_offset += NoteAlignment - 3; // offset is aligned, this can't overflow
+ if (qAddOverflow<T::Off>(next_offset, n_namesz, &next_offset))
+ break;
+ next_offset &= -NoteAlignment;
- if (sh.offset == 0 || (sh.offset + sh.size) > fdlen || sh.size < 1) {
- if (lib)
- lib->errorString = QLibrary::tr("'%1' is an invalid ELF object (%2)")
- .arg(library, QLibrary::tr("missing section data. This is not a library."));
- return Corrupt;
+ next_offset += NoteAlignment - 3; // offset is aligned, this can't overflow
+ if (qAddOverflow<T::Off>(next_offset, n_descsz, &next_offset))
+ break;
+ next_offset &= -NoteAlignment;
+ if (next_offset > end_offset)
+ break;
+
+ if (n_namesz == NoteNameSize && n_descsz >= MinPayloadSize
+ && n_type == QPluginMetaData::ElfNoteHeader::NoteType
+ && memcmp(nhdr + 1, QPluginMetaData::ElfNoteHeader::NoteName, NoteNameSize) == 0) {
+ // yes, it's our note
+ r.pos = offset + PayloadStartDelta;
+ r.length = nhdr->n_descsz;
+ return false;
}
- *pos = sh.offset;
- *sectionlen = sh.size;
- return QtMetaDataSection;
+ offset = next_offset;
}
- s += e_shentsize;
+ return true;
+ };
+ scanProgramHeaders(data, error, noteFinder);
+
+ if (!r.length)
+ return r;
+
+ qEDebug << "found Qt metadata in ELF note at"
+ << Qt::hex << Qt::showbase << r.pos << "size" << Qt::reset << r.length;
+ return r;
+}
+
+static QLibraryScanResult scanSections(QByteArrayView data, const ErrorMaker &error)
+{
+ auto header = reinterpret_cast<const T::Ehdr *>(data.data());
+
+ // in order to find the .qtmetadata section, we need to:
+ // a) find the section table
+ // it's located at offset header->e_shoff
+ // validate it
+ T::Word e_shnum = header->e_shnum;
+ T::Off offset = e_shnum * sizeof(T::Shdr); // can't overflow due to size of T::Half
+ if (qAddOverflow(offset, header->e_shoff, &offset) || offset > size_t(data.size()))
+ return error(QLibrary::tr("section table extends past the end of the file"));
+
+ // b) find the section entry for the section header string table (shstrab)
+ // it's a section whose entry is pointed by e_shstrndx
+ auto sections = reinterpret_cast<const T::Shdr *>(data.data() + header->e_shoff);
+ auto sections_end = sections + e_shnum;
+ auto shdr = sections + header->e_shstrndx;
+
+ // validate the shstrtab
+ offset = shdr->sh_offset;
+ T::Off shstrtab_size = shdr->sh_size;
+ qEDebug << "shstrtab section is located at offset" << offset << "size" << shstrtab_size;
+ if (T::Off end; qAddOverflow<T::Off>(offset, shstrtab_size, &end)
+ || end > size_t(data.size()))
+ return error(QLibrary::tr("section header string table extends past the end of the file"));
+
+ // c) iterate over the sections to find .qtmetadata
+ const char *shstrtab_start = data.data() + offset;
+ shdr = sections;
+ for (int section = 0; shdr != sections_end; ++section, ++shdr) {
+ QLatin1StringView name;
+ if (shdr->sh_name < shstrtab_size) {
+ const char *namestart = shstrtab_start + shdr->sh_name;
+ size_t len = qstrnlen(namestart, shstrtab_size - shdr->sh_name);
+ name = QLatin1StringView(namestart, len);
+ }
+ qEDebug << "section" << section << "name" << name << ElfSectionDebug{shdr};
+
+ // sanity check the section
+ if (name.isNull())
+ return error(QLibrary::tr("a section name extends past the end of the file"));
+
+ // sections aren't allowed to extend past the end of the file, unless
+ // they are NOBITS sections
+ if (shdr->sh_type == SHT_NOBITS)
+ 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"));
+ }
+
+ if (name != ".qtmetadata"_L1)
+ continue;
+ qEDebug << "found .qtmetadata section";
+ if (shdr->sh_size < sizeof(QPluginMetaData::MagicHeader))
+ return error(QLibrary::tr(".qtmetadata section is too small"));
+
+ if (IncludeValidityChecks) {
+ QByteArrayView expectedMagic = QByteArrayView::fromArray(QPluginMetaData::MagicString);
+ QByteArrayView actualMagic = data.sliced(shdr->sh_offset, expectedMagic.size());
+ if (expectedMagic != actualMagic)
+ return error(QLibrary::tr(".qtmetadata section has incorrect magic"));
+
+ if (shdr->sh_flags & SHF_WRITE)
+ return error(QLibrary::tr(".qtmetadata section is writable"));
+ if (shdr->sh_flags & SHF_EXECINSTR)
+ return error(QLibrary::tr(".qtmetadata section is executable"));
+ }
+
+ return { qsizetype(shdr->sh_offset + sizeof(QPluginMetaData::MagicString)),
+ qsizetype(shdr->sh_size - sizeof(QPluginMetaData::MagicString)) };
+ }
+
+ // section .qtmetadata not found
+ return error.notfound();
+}
+
+QLibraryScanResult QElfParser::parse(QByteArrayView data, QString *errMsg)
+{
+ ErrorMaker error(errMsg);
+ if (size_t(data.size()) < sizeof(T::Ehdr)) {
+ qEDebug << "file too small:" << size_t(data.size());
+ return error(QLibrary::tr("file too small"));
+ }
+
+ qEDebug << ElfHeaderDebug{ reinterpret_cast<const uchar *>(data.data()) };
+
+ auto header = reinterpret_cast<const T::Ehdr *>(data.data());
+ if (!ElfHeaderCheck<>::checkHeader(*header))
+ return error(ElfHeaderCheck<>::explainCheckFailure(*header));
+
+ qEDebug << "contains" << header->e_phnum << "program headers of"
+ << header->e_phentsize << "bytes at offset" << header->e_phoff;
+ qEDebug << "contains" << header->e_shnum << "sections of" << header->e_shentsize
+ << "bytes at offset" << header->e_shoff
+ << "; section header string table (shstrtab) is entry" << header->e_shstrndx;
+
+ // some sanity checks
+ if constexpr (IncludeValidityChecks) {
+ if (header->e_phentsize != sizeof(T::Phdr))
+ return error(QLibrary::tr("unexpected program header entry size (%1)")
+ .arg(header->e_phentsize));
+ }
+
+ if (!preScanProgramHeaders(data, error))
+ return {};
+
+ if (QLibraryScanResult r = scanProgramHeadersForNotes(data, error); r.length)
+ return r;
+
+ if (!ElfNotesAreMandatory) {
+ if constexpr (IncludeValidityChecks) {
+ if (header->e_shentsize != sizeof(T::Shdr))
+ return error(QLibrary::tr("unexpected section entry size (%1)")
+ .arg(header->e_shentsize));
+ }
+ if (header->e_shoff == 0 || header->e_shnum == 0) {
+ // this is still a valid ELF file but we don't have a section table
+ qEDebug << "no section table present, not able to find Qt metadata";
+ return error.notfound();
+ }
+
+ if (header->e_shnum && header->e_shstrndx >= header->e_shnum)
+ return error(QLibrary::tr("e_shstrndx greater than the number of sections e_shnum (%1 >= %2)")
+ .arg(header->e_shstrndx).arg(header->e_shnum));
+ return scanSections(data, error);
}
- return NoQtSection;
+ return error.notfound();
}
QT_END_NAMESPACE
-#endif // defined(Q_OF_ELF) && defined(Q_CC_GNU)
+#endif // Q_OF_ELF
diff --git a/src/corelib/plugin/qelfparser_p.h b/src/corelib/plugin/qelfparser_p.h
index bd967e53ae..61498a859a 100644
--- a/src/corelib/plugin/qelfparser_p.h
+++ b/src/corelib/plugin/qelfparser_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QELFPARSER_P_H
#define QELFPARSER_P_H
@@ -52,44 +16,21 @@
//
#include <qendian.h>
-#include <private/qglobal_p.h>
+#include "qlibrary_p.h"
QT_REQUIRE_CONFIG(library);
-#if defined(Q_OF_ELF) && defined(Q_CC_GNU)
+#ifdef Q_OF_ELF
QT_BEGIN_NAMESPACE
-class QString;
-class QLibraryPrivate;
-
-typedef quint16 qelfhalf_t;
-typedef quint32 qelfword_t;
-typedef quintptr qelfoff_t;
-typedef quintptr qelfaddr_t;
-
-class QElfParser
+struct QElfParser
{
-public:
- enum ScanResult { QtMetaDataSection, NoQtSection, NotElf, Corrupt };
- enum { ElfLittleEndian = 0, ElfBigEndian = 1 };
-
- struct ElfSectionHeader
- {
- qelfword_t name;
- qelfword_t type;
- qelfoff_t offset;
- qelfoff_t size;
- };
-
- qelfoff_t m_stringTableFileOffset;
-
- const char *parseSectionHeader(const char* s, ElfSectionHeader *sh);
- ScanResult parse(const char *m_s, ulong fdlen, const QString &library, QLibraryPrivate *lib, qsizetype *pos, qsizetype *sectionlen);
+ static QLibraryScanResult parse(QByteArrayView data, QString *errMsg);
};
QT_END_NAMESPACE
-#endif // defined(Q_OF_ELF) && defined(Q_CC_GNU)
+#endif // Q_OF_ELF
#endif // QELFPARSER_P_H
diff --git a/src/corelib/plugin/qfactoryinterface.cpp b/src/corelib/plugin/qfactoryinterface.cpp
index b503c245c5..1a48d311aa 100644
--- a/src/corelib/plugin/qfactoryinterface.cpp
+++ b/src/corelib/plugin/qfactoryinterface.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qfactoryinterface.h"
diff --git a/src/corelib/plugin/qfactoryinterface.h b/src/corelib/plugin/qfactoryinterface.h
index 86a1c8315f..098b7d4201 100644
--- a/src/corelib/plugin/qfactoryinterface.h
+++ b/src/corelib/plugin/qfactoryinterface.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFACTORYINTERFACE_H
#define QFACTORYINTERFACE_H
@@ -51,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 7e4a9f06fd..e2d9a40cb4 100644
--- a/src/corelib/plugin/qfactoryloader.cpp
+++ b/src/corelib/plugin/qfactoryloader.cpp
@@ -1,120 +1,243 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2018 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2022 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qfactoryloader_p.h"
#ifndef QT_NO_QOBJECT
-#include "qfactoryinterface.h"
-#include "qmap.h"
-#include <qdir.h>
-#include <qdebug.h>
-#include "qmutex.h"
-#include "qplugin.h"
-#include "qplugin_p.h"
-#include "qpluginloader.h"
-#include "private/qobject_p.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 "qdirlisting.h"
+#include "qfileinfo.h"
+#include "qjsonarray.h"
#include "qjsondocument.h"
-#include "qjsonvalue.h"
#include "qjsonobject.h"
-#include "qjsonarray.h"
-#include "private/qduplicatetracker_p.h"
+#include "qmutex.h"
+#include "qplugin.h"
+#include "qplugin_p.h"
+#include "qpluginloader.h"
+
+#if QT_CONFIG(library)
+# include "qlibrary_p.h"
+#endif
#include <qtcore_tracepoints_p.h>
+#include <map>
+#include <vector>
+
QT_BEGIN_NAMESPACE
-static inline int metaDataSignatureLength()
+using namespace Qt::StringLiterals;
+
+Q_TRACE_POINT(qtcore, QFactoryLoader_update, const QString &fileName);
+
+namespace {
+struct IterationResult
{
- return sizeof("QTMETADATA ") - 1;
-}
+ 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;
+ }
-static QJsonDocument jsonFromCborMetaData(const char *raw, qsizetype size, QString *errMsg)
+ 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
{
- // extract the keys not stored in CBOR
- int qt_metadataVersion = quint8(raw[0]);
- int qt_version = qFromBigEndian<quint16>(raw + 1);
- int qt_archRequirements = quint8(raw[3]);
- if (Q_UNLIKELY(raw[-1] != '!' || qt_metadataVersion != 0)) {
- *errMsg = QStringLiteral("Invalid metadata version");
- return QJsonDocument();
+ 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
- raw += 4;
- size -= 4;
- QByteArray ba = QByteArray::fromRawData(raw, int(size));
- QCborParserError err;
- QCborValue metadata = QCborValue::fromCbor(ba, &err);
+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 IterationResult::InvalidMetaDataVersion;
+
+ // use fromRawData to keep QCborStreamReader from copying
+ raw = raw.sliced(sizeof(header));
+ QByteArray ba = QByteArray::fromRawData(raw.data(), raw.size());
+ 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) {
- *errMsg = QLatin1String("Metadata parsing error: ") + err.error.toString();
- return QJsonDocument();
+ if (QCborError e = reader.lastError())
+ return e;
+ if (r != IterationResult::ContinueSearch)
+ return r;
}
- if (!metadata.isMap()) {
- *errMsg = QStringLiteral("Unexpected metadata contents");
- return QJsonDocument();
+ 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"));
}
- QJsonObject o;
- o.insert(QLatin1String("version"), qt_version << 8);
- o.insert(QLatin1String("debug"), bool(qt_archRequirements & 1));
- o.insert(QLatin1String("archreq"), qt_archRequirements);
+ // header was validated
+ auto header = qFromUnaligned<QPluginMetaData::Header>(raw.data());
+
+ DecodedArchRequirements archReq =
+ header.version == 0 ? decodeVersion0ArchRequirements(header.plugin_arch_requirements)
+ : decodeVersion1ArchRequirements(header.plugin_arch_requirements);
+
+ // insert the keys not stored in the top-level CBOR map
+ map[int(QtPluginMetaDataKeys::QtVersion)] =
+ QT_VERSION_CHECK(header.qt_major_version, header.qt_minor_version, 0);
+ map[int(QtPluginMetaDataKeys::IsDebug)] = archReq.isDebug;
+ map[int(QtPluginMetaDataKeys::Requirements)] = archReq.level;
- // convert the top-level map integer keys
- for (auto it : metadata.toMap()) {
+ data = std::move(map);
+ return true;
+}
+
+QJsonObject QPluginParsedMetaData::toJson() const
+{
+ // convert from the internal CBOR representation to an external JSON one
+ QJsonObject o;
+ for (auto it : data.toMap()) {
QString key;
if (it.first.isInteger()) {
switch (it.first.toInteger()) {
#define CONVERT_TO_STRING(IntKey, StringKey, Description) \
case int(IntKey): key = QStringLiteral(StringKey); break;
QT_PLUGIN_FOREACH_METADATA(CONVERT_TO_STRING)
-#undef CONVERT_TO_STRING
-
- case int(QtPluginMetaDataKeys::Requirements):
- // special case: recreate the debug key
- o.insert(QLatin1String("debug"), bool(it.second.toInteger() & 1));
- key = QStringLiteral("archreq");
- break;
}
} else {
key = it.first.toString();
@@ -123,194 +246,195 @@ static QJsonDocument jsonFromCborMetaData(const char *raw, qsizetype size, QStri
if (!key.isEmpty())
o.insert(key, it.second.toJsonValue());
}
- return QJsonDocument(o);
-}
-
-QJsonDocument qJsonFromRawLibraryMetaData(const char *raw, qsizetype sectionSize, QString *errMsg)
-{
- raw += metaDataSignatureLength();
- sectionSize -= metaDataSignatureLength();
-
- return jsonFromCborMetaData(raw, sectionSize, errMsg);
+ return o;
}
class QFactoryLoaderPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QFactoryLoader)
+ Q_DISABLE_COPY_MOVE(QFactoryLoaderPrivate)
public:
QFactoryLoaderPrivate() { }
QByteArray iid;
#if QT_CONFIG(library)
~QFactoryLoaderPrivate();
mutable QMutex mutex;
- QList<QLibraryPrivate*> libraryList;
- QMap<QString,QLibraryPrivate*> keyMap;
+ QDuplicateTracker<QString> loadedPaths;
+ std::vector<QLibraryPrivate::UniquePtr> libraries;
+ std::map<QString, QLibraryPrivate*> keyMap;
QString suffix;
+ QString extraSearchPath;
Qt::CaseSensitivity cs;
- QDuplicateTracker<QString> loadedPaths;
+
+ void updateSinglePath(const QString &pluginDir);
#endif
};
#if QT_CONFIG(library)
-Q_GLOBAL_STATIC(QList<QFactoryLoader *>, qt_factory_loaders)
-
-Q_GLOBAL_STATIC(QRecursiveMutex, qt_factoryloader_mutex)
+static Q_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(lcFactoryLoader, "QT_DEBUG_PLUGINS",
+ "qt.core.plugin.factoryloader")
-QFactoryLoaderPrivate::~QFactoryLoaderPrivate()
+namespace {
+struct QFactoryLoaderGlobals
{
- for (QLibraryPrivate *library : qAsConst(libraryList))
- library->release();
+ // needs to be recursive because loading one plugin could cause another
+ // factory to be initialized
+ QRecursiveMutex mutex;
+ QList<QFactoryLoader *> loaders;
+};
}
-void QFactoryLoader::update()
-{
-#ifdef QT_SHARED
- Q_D(QFactoryLoader);
- QStringList paths = QCoreApplication::libraryPaths();
- for (int i = 0; i < paths.count(); ++i) {
- const QString &pluginDir = paths.at(i);
- // Already loaded, skip it...
- if (d->loadedPaths.hasSeen(pluginDir))
- continue;
+Q_GLOBAL_STATIC(QFactoryLoaderGlobals, qt_factoryloader_global)
-#ifdef Q_OS_ANDROID
- QString path = pluginDir;
-#else
- QString path = pluginDir + d->suffix;
-#endif
+QFactoryLoaderPrivate::~QFactoryLoaderPrivate()
+ = default;
- if (qt_debug_component())
- qDebug() << "QFactoryLoader::QFactoryLoader() checking directory path" << path << "...";
+inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path)
+{
+ struct LibraryReleaser {
+ void operator()(QLibraryPrivate *library)
+ { if (library) library->release(); }
+ };
- if (!QDir(path).exists(QLatin1String(".")))
- continue;
+ // If we've already loaded, skip it...
+ if (loadedPaths.hasSeen(path))
+ return;
- QStringList plugins = QDir(path).entryList(
+ qCDebug(lcFactoryLoader) << "checking directory path" << path << "...";
+
+ QDirListing plugins(path,
#if defined(Q_OS_WIN)
- QStringList(QStringLiteral("*.dll")),
+ QStringList(QStringLiteral("*.dll")),
#elif defined(Q_OS_ANDROID)
- QStringList(QLatin1String("libplugins_%1_*.so").arg(d->suffix)),
+ QStringList("libplugins_%1_*.so"_L1.arg(suffix)),
#endif
- QDir::Files);
- QLibraryPrivate *library = nullptr;
-
- for (int j = 0; j < plugins.count(); ++j) {
- QString fileName = QDir::cleanPath(path + QLatin1Char('/') + plugins.at(j));
-
-#ifdef Q_OS_MAC
- const bool isDebugPlugin = fileName.endsWith(QLatin1String("_debug.dylib"));
- const bool isDebugLibrary =
- #ifdef QT_DEBUG
- true;
- #else
- false;
- #endif
-
- // Skip mismatching plugins so that we don't end up loading both debug and release
- // versions of the same Qt libraries (due to the plugin's dependencies).
- if (isDebugPlugin != isDebugLibrary)
- continue;
+ QDir::Files);
+
+ 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
+ true;
+ #else
+ false;
+ #endif
+
+ // Skip mismatching plugins so that we don't end up loading both debug and release
+ // versions of the same Qt libraries (due to the plugin's dependencies).
+ if (isDebugPlugin != isDebugLibrary)
+ continue;
#elif defined(Q_PROCESSOR_X86)
- if (fileName.endsWith(QLatin1String(".avx2")) || fileName.endsWith(QLatin1String(".avx512"))) {
- // ignore AVX2-optimized file, we'll do a bait-and-switch to it later
- continue;
- }
+ if (fileName.endsWith(".avx2"_L1) || fileName.endsWith(".avx512"_L1)) {
+ // ignore AVX2-optimized file, we'll do a bait-and-switch to it later
+ continue;
+ }
#endif
- if (qt_debug_component()) {
- qDebug() << "QFactoryLoader::QFactoryLoader() looking at" << fileName;
- }
+ qCDebug(lcFactoryLoader) << "looking at" << fileName;
- Q_TRACE(QFactoryLoader_update, fileName);
-
- library = QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath());
- if (!library->isPlugin()) {
- if (qt_debug_component()) {
- qDebug() << library->errorString << Qt::endl
- << " not a plugin";
- }
- library->release();
- continue;
- }
+ Q_TRACE(QFactoryLoader_update, fileName);
- QStringList keys;
- bool metaDataOk = false;
+ QLibraryPrivate::UniquePtr library;
+ library.reset(QLibraryPrivate::findOrCreate(dirEntry.canonicalFilePath()));
+ if (!library->isPlugin()) {
+ qCDebug(lcFactoryLoader) << library->errorString << Qt::endl
+ << " not a plugin";
+ continue;
+ }
- QString iid = library->metaData.value(QLatin1String("IID")).toString();
- if (iid == QLatin1String(d->iid.constData(), d->iid.size())) {
- QJsonObject object = library->metaData.value(QLatin1String("MetaData")).toObject();
- metaDataOk = true;
+ QStringList keys;
+ bool metaDataOk = false;
- QJsonArray k = object.value(QLatin1String("Keys")).toArray();
- for (int i = 0; i < k.size(); ++i)
- keys += d->cs ? k.at(i).toString() : k.at(i).toString().toLower();
- }
- if (qt_debug_component())
- qDebug() << "Got keys from plugin meta data" << keys;
+ QString iid = library->metaData.value(QtPluginMetaDataKeys::IID).toString();
+ if (iid == QLatin1StringView(this->iid.constData(), this->iid.size())) {
+ QCborMap object = library->metaData.value(QtPluginMetaDataKeys::MetaData).toMap();
+ metaDataOk = true;
+ const QCborArray k = object.value("Keys"_L1).toArray();
+ for (QCborValueConstRef v : k)
+ keys += cs ? v.toString() : v.toString().toLower();
+ }
+ qCDebug(lcFactoryLoader) << "Got keys from plugin meta data" << keys;
- if (!metaDataOk) {
- library->release();
- continue;
- }
+ if (!metaDataOk)
+ continue;
- int keyUsageCount = 0;
- for (int k = 0; k < keys.count(); ++k) {
- // first come first serve, unless the first
- // library was built with a future Qt version,
- // whereas the new one has a Qt version that fits
- // better
- const QString &key = keys.at(k);
- QLibraryPrivate *previous = d->keyMap.value(key);
- int prev_qt_version = 0;
- if (previous) {
- prev_qt_version = (int)previous->metaData.value(QLatin1String("version")).toDouble();
- }
- int qt_version = (int)library->metaData.value(QLatin1String("version")).toDouble();
- if (!previous || (prev_qt_version > QT_VERSION && qt_version <= QT_VERSION)) {
- d->keyMap[key] = library;
- ++keyUsageCount;
- }
- }
- if (keyUsageCount || keys.isEmpty()) {
- library->setLoadHints(QLibrary::PreventUnloadHint); // once loaded, don't unload
- QMutexLocker locker(&d->mutex);
- d->libraryList += library;
- } else {
- library->release();
+ int keyUsageCount = 0;
+ for (const QString &key : std::as_const(keys)) {
+ // first come first serve, unless the first
+ // library was built with a future Qt version,
+ // 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[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)) {
+ previous = library.get(); // we WILL .release()
+ ++keyUsageCount;
}
}
+ if (keyUsageCount || keys.isEmpty()) {
+ library->setLoadHints(QLibrary::PreventUnloadHint); // once loaded, don't unload
+ QMutexLocker locker(&mutex);
+ libraries.push_back(std::move(library));
+ }
+ };
+}
+
+void QFactoryLoader::update()
+{
+#ifdef QT_SHARED
+ Q_D(QFactoryLoader);
+
+ const QStringList paths = QCoreApplication::libraryPaths();
+ for (const QString &pluginDir : paths) {
+#ifdef Q_OS_ANDROID
+ QString path = pluginDir;
+#else
+ QString path = pluginDir + d->suffix;
+#endif
+
+ d->updateSinglePath(path);
}
+ if (!d->extraSearchPath.isEmpty())
+ d->updateSinglePath(d->extraSearchPath);
#else
Q_D(QFactoryLoader);
- if (qt_debug_component()) {
- qDebug() << "QFactoryLoader::QFactoryLoader() ignoring" << d->iid
- << "since plugins are disabled in static builds";
- }
+ qCDebug(lcFactoryLoader) << "ignoring" << d->iid
+ << "since plugins are disabled in static builds";
#endif
}
QFactoryLoader::~QFactoryLoader()
{
- QMutexLocker locker(qt_factoryloader_mutex());
- qt_factory_loaders()->removeAll(this);
+ if (!qt_factoryloader_global.isDestroyed()) {
+ QMutexLocker locker(&qt_factoryloader_global->mutex);
+ qt_factoryloader_global->loaders.removeOne(this);
+ }
}
-#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
void QFactoryLoader::refreshAll()
{
- QMutexLocker locker(qt_factoryloader_mutex());
- QList<QFactoryLoader *> *loaders = qt_factory_loaders();
- for (QList<QFactoryLoader *>::const_iterator it = loaders->constBegin();
- it != loaders->constEnd(); ++it) {
- (*it)->update();
+ if (qt_factoryloader_global.exists()) {
+ QMutexLocker locker(&qt_factoryloader_global->mutex);
+ for (QFactoryLoader *loader : std::as_const(qt_factoryloader_global->loaders))
+ loader->update();
}
}
@@ -321,6 +445,9 @@ QFactoryLoader::QFactoryLoader(const char *iid,
Qt::CaseSensitivity cs)
: QObject(*new QFactoryLoaderPrivate)
{
+ Q_ASSERT_X(suffix.startsWith(u'/'), "QFactoryLoader",
+ "For historical reasons, the suffix must start with '/' (and it can't be empty)");
+
moveToThread(QCoreApplicationPrivate::mainThread());
Q_D(QFactoryLoader);
d->iid = iid;
@@ -328,36 +455,93 @@ QFactoryLoader::QFactoryLoader(const char *iid,
d->cs = cs;
d->suffix = suffix;
# ifdef Q_OS_ANDROID
- if (!d->suffix.isEmpty() && d->suffix.at(0) == QLatin1Char('/'))
+ if (!d->suffix.isEmpty() && d->suffix.at(0) == u'/')
d->suffix.remove(0, 1);
# endif
- QMutexLocker locker(qt_factoryloader_mutex());
+ QMutexLocker locker(&qt_factoryloader_global->mutex);
update();
- qt_factory_loaders()->append(this);
+ qt_factoryloader_global->loaders.append(this);
#else
Q_UNUSED(suffix);
Q_UNUSED(cs);
#endif
}
-QList<QJsonObject> QFactoryLoader::metaData() const
+void QFactoryLoader::setExtraSearchPath(const QString &path)
+{
+#if QT_CONFIG(library)
+ Q_D(QFactoryLoader);
+ if (d->extraSearchPath == path)
+ return; // nothing to do
+
+ QMutexLocker locker(&qt_factoryloader_global->mutex);
+ QString oldPath = std::exchange(d->extraSearchPath, path);
+ if (oldPath.isEmpty()) {
+ // easy case, just update this directory
+ d->updateSinglePath(d->extraSearchPath);
+ } else {
+ // must re-scan everything
+ d->loadedPaths.clear();
+ d->libraries.clear();
+ d->keyMap.clear();
+ update();
+ }
+#else
+ Q_UNUSED(path);
+#endif
+}
+
+QFactoryLoader::MetaDataList QFactoryLoader::metaData() const
{
Q_D(const QFactoryLoader);
- QList<QJsonObject> metaData;
+ QList<QPluginParsedMetaData> metaData;
#if QT_CONFIG(library)
QMutexLocker locker(&d->mutex);
- for (int i = 0; i < d->libraryList.size(); ++i)
- metaData.append(d->libraryList.at(i)->metaData);
+ for (const auto &library : d->libraries)
+ metaData.append(library->metaData);
#endif
+ QLatin1StringView iid(d->iid.constData(), d->iid.size());
const auto staticPlugins = QPluginLoader::staticPlugins();
for (const QStaticPlugin &plugin : staticPlugins) {
- const QJsonObject object = plugin.metaData();
- if (object.value(QLatin1String("IID")) != QLatin1String(d->iid.constData(), d->iid.size()))
+ QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), plugin.rawMetaDataSize);
+ QPluginParsedMetaData parsed(pluginData);
+ if (parsed.isError() || parsed.value(QtPluginMetaDataKeys::IID) != iid)
continue;
- metaData.append(object);
+ 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;
}
@@ -369,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());
@@ -378,18 +562,20 @@ 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
- QList<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins();
- for (int i = 0; i < staticPlugins.count(); ++i) {
- const QJsonObject object = staticPlugins.at(i).metaData();
- if (object.value(QLatin1String("IID")) != QLatin1String(d->iid.constData(), d->iid.size()))
+ QLatin1StringView iid(d->iid.constData(), d->iid.size());
+ const QList<QStaticPlugin> staticPlugins = QPluginLoader::staticPlugins();
+ for (QStaticPlugin plugin : staticPlugins) {
+ QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), plugin.rawMetaDataSize);
+ if (!isIidMatch(pluginData, iid))
continue;
if (index == 0)
- return staticPlugins.at(i).instance();
+ return plugin.instance();
--index;
}
@@ -399,26 +585,22 @@ QObject *QFactoryLoader::instance(int index) const
QMultiMap<int, QString> QFactoryLoader::keyMap() const
{
QMultiMap<int, QString> result;
- const QList<QJsonObject> metaDataList = metaData();
- for (int i = 0; i < metaDataList.size(); ++i) {
- const QJsonObject metaData = metaDataList.at(i).value(QLatin1String("MetaData")).toObject();
- const QJsonArray keys = metaData.value(QLatin1String("Keys")).toArray();
- const int keyCount = keys.size();
- for (int k = 0; k < keyCount; ++k)
- result.insert(i, keys.at(k).toString());
+ 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());
}
return result;
}
int QFactoryLoader::indexOf(const QString &needle) const
{
- const QList<QJsonObject> metaDataList = metaData();
- for (int i = 0; i < metaDataList.size(); ++i) {
- const QJsonObject metaData = metaDataList.at(i).value(QLatin1String("MetaData")).toObject();
- const QJsonArray keys = metaData.value(QLatin1String("Keys")).toArray();
- const int keyCount = keys.size();
- for (int k = 0; k < keyCount; ++k) {
- if (!keys.at(k).toString().compare(needle, Qt::CaseInsensitive))
+ 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 7815ea0b5d..56dc7e6ad1 100644
--- a/src/corelib/plugin/qfactoryloader_p.h
+++ b/src/corelib/plugin/qfactoryloader_p.h
@@ -1,41 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 The Qt Company Ltd.
+// Copyright (C) 2022 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QFACTORYLOADER_P_H
#define QFACTORYLOADER_P_H
@@ -54,20 +19,43 @@
#include "QtCore/qglobal.h"
#ifndef QT_NO_QOBJECT
-#include "QtCore/qobject.h"
-#include "QtCore/qstringlist.h"
+#include "QtCore/private/qplugin_p.h"
+#include "QtCore/qcbormap.h"
#include "QtCore/qcborvalue.h"
-#include "QtCore/qjsonobject.h"
-#include "QtCore/qjsondocument.h"
#include "QtCore/qmap.h"
-#include "QtCore/qendian.h"
-#if QT_CONFIG(library)
-#include "private/qlibrary_p.h"
-#endif
+#include "QtCore/qobject.h"
+#include "QtCore/qplugin.h"
QT_BEGIN_NAMESPACE
-QJsonDocument qJsonFromRawLibraryMetaData(const char *raw, qsizetype size, QString *errMsg);
+class QJsonObject;
+class QLibraryPrivate;
+
+class QPluginParsedMetaData
+{
+ QCborValue data;
+ bool setError(const QString &errorString) Q_DECL_COLD_FUNCTION
+ {
+ data = errorString;
+ return false;
+ }
+public:
+ QPluginParsedMetaData() = default;
+ QPluginParsedMetaData(QByteArrayView input) { parse(input); }
+
+ bool isError() const { return !data.isMap(); }
+ QString errorString() const { return data.toString(); }
+
+ bool parse(QByteArrayView input);
+ bool parse(QPluginMetaData metaData)
+ { return parse(QByteArrayView(reinterpret_cast<const char *>(metaData.data), metaData.size)); }
+
+ QJsonObject toJson() const; // only for QLibrary & QPluginLoader
+
+ // if data is not a map, toMap() returns empty, so shall these functions
+ QCborMap toCbor() const { return data.toMap(); }
+ QCborValue value(QtPluginMetaDataKeys k) const { return data[int(k)]; }
+};
class QFactoryLoaderPrivate;
class Q_CORE_EXPORT QFactoryLoader : public QObject
@@ -86,15 +74,19 @@ 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);
QMultiMap<int, QString> keyMap() const;
int indexOf(const QString &needle) const;
- QList<QJsonObject> metaData() const;
+ 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 b67c3b6439..a3ef8e3c52 100644
--- a/src/corelib/plugin/qlibrary.cpp
+++ b/src/corelib/plugin/qlibrary.cpp
@@ -1,86 +1,61 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2018 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-#include "qplatformdefs.h"
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2021 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qlibrary.h"
-
-#include "qfactoryloader_p.h"
#include "qlibrary_p.h"
-#include <qstringlist.h>
+
+#include <q20algorithm.h>
+#include <qbytearraymatcher.h>
+#include <qdebug.h>
+#include <qendian.h>
#include <qfile.h>
#include <qfileinfo.h>
+#include <qjsondocument.h>
#include <qmutex.h>
-#include <qmap.h>
-#include <private/qcoreapplication_p.h>
-#include <private/qsystemerror_p.h>
-#ifdef Q_OS_MAC
+#include <qoperatingsystemversion.h>
+#include <qstringlist.h>
+
+#ifdef Q_OS_DARWIN
# include <private/qcore_mac_p.h>
#endif
-#ifndef NO_ERRNO_H
-#include <errno.h>
-#endif // NO_ERROR_H
-#include <qdebug.h>
-#include <qlist.h>
-#include <qdir.h>
-#include <qendian.h>
-#include <qjsondocument.h>
-#include <qjsonvalue.h>
+#include <private/qcoreapplication_p.h>
+#include <private/qloggingregistry_p.h>
+#include <private/qsystemerror_p.h>
+
+#include "qcoffpeparser_p.h"
#include "qelfparser_p.h"
+#include "qfactoryloader_p.h"
#include "qmachparser_p.h"
#include <qtcore_tracepoints_p.h>
-#include <QtCore/q20algorithm.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?)
+static constexpr bool PluginMustMatchQtDebug =
+ QOperatingSystemVersion::currentType() == QOperatingSystemVersion::Windows
+#if defined(Q_CC_MINGW)
+ && QT_CONFIG(debug_and_release)
+#endif
+ ;
+
#ifdef QT_NO_DEBUG
-# define QLIBRARY_AS_DEBUG false
+static constexpr bool QtBuildIsDebug = false;
#else
-# define QLIBRARY_AS_DEBUG true
+static constexpr bool QtBuildIsDebug = true;
#endif
-#if defined(Q_OS_UNIX) || (defined(Q_CC_MINGW) && !QT_CONFIG(debug_and_release))
-// We don't use separate debug and release libs on UNIX, so we want
-// to allow loading plugins, regardless of how they were built.
-# define QT_NO_DEBUG_PLUGIN_CHECK
-#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")
/*!
\class QLibrary
@@ -187,43 +162,43 @@ QT_BEGIN_NAMESPACE
\sa loadHints
*/
-
-static qsizetype qt_find_pattern(const char *s, qsizetype s_len,
- const char *pattern, ulong p_len)
+static QLibraryScanResult qt_find_pattern(const char *s, qsizetype s_len, QString *errMsg)
{
/*
- we search from the end of the file because on the supported
- systems, the read-only data/text segments are placed at the end
- of the file. HOWEVER, when building with debugging enabled, all
- the debug symbols are placed AFTER the data/text segments.
-
- what does this mean? when building in release mode, the search
- is fast because the data we are looking for is at the end of the
- file... when building in debug mode, the search is slower
- because we have to skip over all the debugging symbols first
- */
+ We used to search from the end of the file so we'd skip the code and find
+ the read-only data that usually follows. Unfortunately, in debug builds,
+ the debug sections come after and are usually much bigger than everything
+ else, making this process slower than necessary with debug plugins.
- if (!s || !pattern || qsizetype(p_len) > s_len)
- return -1;
-
- size_t i, hs = 0, hp = 0, delta = s_len - p_len;
-
- for (i = 0; i < p_len; ++i) {
- hs += s[delta + i];
- hp += pattern[i];
- }
- i = delta;
- for (;;) {
- if (hs == hp && qstrncmp(s + i, pattern, p_len) == 0)
- return i; // can't overflow, by construction
- if (i == 0)
- break;
- --i;
- hs -= s[i + p_len];
- hs += s[i];
+ More importantly, the pattern string may exist in the debug information due
+ to it being used in the plugin in the first place.
+ */
+#if defined(Q_OF_ELF)
+ return QElfParser::parse({s, s_len}, errMsg);
+#elif defined(Q_OF_MACH_O)
+ return QMachOParser::parse(s, s_len, errMsg);
+#elif defined(Q_OS_WIN)
+ return QCoffPeParser::parse({s, s_len}, errMsg);
+#else
+# warning "Qt does not know how to efficiently parse your platform's binary format; using slow fall-back."
+#endif
+ static constexpr auto matcher = [] {
+ // QPluginMetaData::MagicString is not NUL-terminated, but
+ // qMakeStaticByteArrayMatcher requires its argument to be, so
+ // duplicate here, but statically check we didn't mess up:
+ constexpr auto &pattern = "QTMETADATA !";
+ constexpr auto magic = std::string_view(QPluginMetaData::MagicString,
+ sizeof(QPluginMetaData::MagicString));
+ static_assert(pattern == magic);
+ return qMakeStaticByteArrayMatcher(pattern);
+ }();
+ qsizetype i = matcher.indexIn({s, s_len});
+ if (i < 0) {
+ *errMsg = QLibrary::tr("'%1' is not a Qt plugin").arg(*errMsg);
+ return QLibraryScanResult{};
}
-
- return -1;
+ i += sizeof(QPluginMetaData::MagicString);
+ return { i, s_len - i };
}
/*
@@ -236,17 +211,15 @@ static qsizetype qt_find_pattern(const char *s, qsizetype s_len,
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)) {
if (lib)
lib->errorString = file.errorString();
- if (qt_debug_component()) {
- qWarning("%s: %ls", QFile::encodeName(library).constData(),
- qUtf16Printable(QSystemError::stdString()));
- }
- return false;
+ qCWarning(qt_lcDebugPlugins, "%ls: cannot open: %ls", qUtf16Printable(library),
+ qUtf16Printable(file.errorString()));
+ return {};
}
// Files can be bigger than the virtual memory size on 32-bit systems, so
@@ -254,88 +227,54 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib)
constexpr qint64 MaxMemoryMapSize =
Q_INT64_C(1) << (sizeof(qsizetype) > 4 ? 40 : 29);
- QByteArray data;
qsizetype fdlen = qMin(file.size(), MaxMemoryMapSize);
const char *filedata = reinterpret_cast<char *>(file.map(0, fdlen));
+#ifdef Q_OS_UNIX
if (filedata == nullptr) {
- // Try reading the data into memory instead (up to 64 MB).
+ // If we can't mmap(), then the dynamic loader won't be able to either.
+ // This can't be used as a plugin.
+ qCWarning(qt_lcDebugPlugins, "%ls: failed to map to memory: %ls",
+ qUtf16Printable(library), qUtf16Printable(file.errorString()));
+ return {};
+ }
+#else
+ QByteArray data;
+ if (filedata == nullptr) {
+ // It's unknown at this point whether Windows supports LoadLibrary() on
+ // files that fail to CreateFileMapping / MapViewOfFile, so we err on
+ // the side of doing a regular read into memory (up to 64 MB).
data = file.read(64 * 1024 * 1024);
filedata = data.constData();
fdlen = data.size();
}
+#endif
- /*
- ELF and Mach-O binaries with GCC have .qplugin sections.
- */
- bool hasMetaData = false;
- qsizetype pos = 0;
- char pattern[] = "qTMETADATA ";
- pattern[0] = 'Q'; // Ensure the pattern "QTMETADATA" is not found in this library should QPluginLoader ever encounter it.
- const ulong plen = ulong(qstrlen(pattern));
-#if defined (Q_OF_ELF)
- QElfParser::ScanResult r = QElfParser().parse(filedata, fdlen, library, lib, &pos, &fdlen);
- if (r == QElfParser::Corrupt || r == QElfParser::NotElf) {
- if (lib && qt_debug_component()) {
- qWarning("QElfParser: %ls", qUtf16Printable(lib->errorString));
- }
- return false;
- } else if (r == QElfParser::QtMetaDataSection) {
- qsizetype rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen);
- if (rel < 0)
- pos = -1;
- else
- pos += rel;
- hasMetaData = true;
- }
-#elif defined(Q_OF_MACH_O)
- {
- QString errorString;
- int r = QMachOParser::parse(filedata, fdlen, library, &errorString, &pos, &fdlen);
- if (r == QMachOParser::NotSuitable) {
- if (qt_debug_component())
- qWarning("QMachOParser: %ls", qUtf16Printable(errorString));
- if (lib)
- lib->errorString = errorString;
- return false;
- }
- // even if the metadata section was not found, the Mach-O parser will
- // at least return the boundaries of the right architecture
- qsizetype rel = qt_find_pattern(filedata + pos, fdlen, pattern, plen);
- if (rel < 0)
- pos = -1;
- else
- pos += rel;
- hasMetaData = true;
- }
-#else
- pos = qt_find_pattern(filedata, fdlen, pattern, plen);
- if (pos > 0)
- hasMetaData = true;
-#endif // defined(Q_OF_ELF) && defined(Q_CC_GNU)
-
- bool ret = false;
-
- if (pos >= 0 && hasMetaData) {
- const char *data = filedata + pos;
- QString errMsg;
- QJsonDocument doc = qJsonFromRawLibraryMetaData(data, fdlen, &errMsg);
- if (doc.isNull()) {
- qWarning("Found invalid metadata in lib %ls: %ls",
- qUtf16Printable(library), qUtf16Printable(errMsg));
+ 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();
+ qCDebug(qt_lcDebugPlugins, "Found invalid metadata in lib %ls: %ls",
+ qUtf16Printable(library), qUtf16Printable(errMsg));
} else {
- lib->metaData = doc.object();
- if (qt_debug_component())
- qWarning("Found metadata in lib %s, metadata=\n%s\n",
- library.toLocal8Bit().constData(), doc.toJson().constData());
- ret = !doc.isNull();
+ qCDebug(qt_lcDebugPlugins, "Found metadata in lib %ls, metadata=\n%s\n",
+ qUtf16Printable(library),
+ QJsonDocument(lib->metaData.toJson()).toJson().constData());
+ return r;
}
+ } else {
+ qCDebug(qt_lcDebugPlugins, "Failed to find metadata in lib %ls: %ls",
+ qUtf16Printable(library), qUtf16Printable(errMsg));
}
- if (!ret && lib)
- lib->errorString = QLibrary::tr("Failed to extract plugin meta data from '%1'").arg(library);
- file.close();
- return ret;
+ lib->errorString = QLibrary::tr("Failed to extract plugin meta data from '%1': %2")
+ .arg(library, errMsg);
+ return {};
}
static void installCoverageTool(QLibraryPrivate *libPrivate)
@@ -356,8 +295,7 @@ static void installCoverageTool(QLibraryPrivate *libPrivate)
int ret = __coveragescanner_register_library(libPrivate->fileName.toLocal8Bit());
- if (qt_debug_component()) {
- if (ret >= 0) {
+ if (ret >= 0) {
qDebug("coverage data for %ls registered",
qUtf16Printable(libPrivate->fileName));
} else {
@@ -384,13 +322,13 @@ 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;
};
-static QBasicMutex qt_library_mutex;
-static QLibraryStore *qt_library_data = nullptr;
-static bool qt_library_data_once;
+Q_CONSTINIT static QBasicMutex qt_library_mutex;
+Q_CONSTINIT static QLibraryStore *qt_library_data = nullptr;
+Q_CONSTINIT static bool qt_library_data_once;
QLibraryStore::~QLibraryStore()
{
@@ -404,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.
@@ -425,17 +356,17 @@ inline void QLibraryStore::cleanup()
lib->unload();
#endif
}
- delete lib;
- it.value() = nullptr;
+ delete std::exchange(lib, nullptr);
}
}
- if (qt_debug_component()) {
- // dump all objects that remain
- for (QLibraryPrivate *lib : qAsConst(data->libraryMap)) {
+ // dump all objects that remain
+ if (lcDebugLibrary().isDebugEnabled()) {
+ for (auto &[_, lib] : data->libraryMap) {
if (lib)
- qDebug() << "On QtCore unload," << lib->fileName << "was leaked, with"
- << lib->libraryRefCount.loadRelaxed() << "users";
+ qDebug(lcDebugLibrary)
+ << "On QtCore unload," << lib->fileName << "was leaked, with"
+ << lib->libraryRefCount.loadRelaxed() << "users";
}
}
@@ -462,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;
}
@@ -497,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;
}
@@ -528,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)
@@ -540,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);
@@ -584,13 +535,9 @@ bool QLibraryPrivate::load()
Q_TRACE(QLibraryPrivate_load_entry, fileName);
bool ret = load_sys();
- if (qt_debug_component()) {
- if (ret) {
- qDebug() << "loaded library" << fileName;
- } else {
- qDebug() << qUtf8Printable(errorString);
- }
- }
+ qCDebug(lcDebugLibrary)
+ << fileName
+ << (ret ? "loaded library" : qUtf8Printable(u"cannot load: " + errorString));
if (ret) {
//when loading a library we add a reference to it so that the QLibraryPrivate won't get deleted
//this allows to unload the library at a later time
@@ -612,9 +559,8 @@ bool QLibraryPrivate::unload(UnloadFlag flag)
QMutexLocker locker(&mutex);
delete inst.data();
if (flag == NoUnloadSys || unload_sys()) {
- if (qt_debug_component())
- qWarning() << "QLibraryPrivate::unload succeeded on" << fileName
- << (flag == NoUnloadSys ? "(faked)" : "");
+ qCDebug(lcDebugLibrary) << fileName << "unloaded library"
+ << (flag == NoUnloadSys ? "(faked)" : "");
// when the library is unloaded, we release the reference on it so that 'this'
// can get deleted
libraryRefCount.deref();
@@ -645,8 +591,7 @@ QtPluginInstanceFunction QLibraryPrivate::loadPlugin()
instanceFactory.storeRelease(ptr); // two threads may store the same value
return ptr;
}
- if (qt_debug_component())
- qWarning() << "QLibraryPrivate::loadPlugin failed on" << fileName << ":" << errorString;
+ qCDebug(qt_lcDebugPlugins) << "QLibraryPrivate::loadPlugin failed on" << fileName << ":" << errorString;
pluginState = IsNotAPlugin;
return nullptr;
}
@@ -669,40 +614,41 @@ QtPluginInstanceFunction QLibraryPrivate::loadPlugin()
bool QLibrary::isLibrary(const QString &fileName)
{
#if defined(Q_OS_WIN)
- return fileName.endsWith(QLatin1String(".dll"), Qt::CaseInsensitive);
+ return fileName.endsWith(".dll"_L1, Qt::CaseInsensitive);
#else // Generic Unix
# if defined(Q_OS_DARWIN)
// On Apple platforms, dylib look like libmylib.1.0.0.dylib
- if (fileName.endsWith(QLatin1String(".dylib")))
+ if (fileName.endsWith(".dylib"_L1))
return true;
# endif
QString completeSuffix = QFileInfo(fileName).completeSuffix();
if (completeSuffix.isEmpty())
return false;
- auto isValidSuffix = [](QStringView s) {
- // if this throws an empty-array error, you need to fix the #ifdef's:
- const QLatin1String candidates[] = {
+ // if this throws an empty-array error, you need to fix the #ifdef's:
+ const QLatin1StringView candidates[] = {
# if defined(Q_OS_HPUX)
/*
See "HP-UX Linker and Libraries User's Guide", section "Link-time Differences between PA-RISC and IPF":
"In PA-RISC (PA-32 and PA-64) shared libraries are suffixed with .sl. In IPF (32-bit and 64-bit),
the shared libraries are suffixed with .so. For compatibility, the IPF linker also supports the .sl suffix."
- */
- QLatin1String("sl"),
+*/
+ "sl"_L1,
# if defined __ia64
- QLatin1String("so"),
+ "so"_L1,
# endif
# elif defined(Q_OS_AIX)
- QLatin1String("a"),
- QLatin1String("so"),
+ "a"_L1,
+ "so"_L1,
# elif defined(Q_OS_DARWIN)
- QLatin1String("so"),
- QLatin1String("bundle"),
+ "so"_L1,
+ "bundle"_L1,
# elif defined(Q_OS_UNIX)
- QLatin1String("so"),
+ "so"_L1,
# endif
- }; // candidates
+ }; // candidates
+
+ auto isValidSuffix = [&candidates](QStringView s) {
return std::find(std::begin(candidates), std::end(candidates), s) != std::end(candidates);
};
@@ -729,21 +675,38 @@ bool QLibrary::isLibrary(const QString &fileName)
static bool qt_get_metadata(QLibraryPrivate *priv, QString *errMsg)
{
- auto getMetaData = [](QFunctionPointer fptr) {
- auto f = reinterpret_cast<QPluginMetaData (*)()>(fptr);
- return f();
+ auto error = [=](QString &&explanation) {
+ *errMsg = QLibrary::tr("'%1' is not a Qt plugin (%2)").arg(priv->fileName, std::move(explanation));
+ return false;
};
- QFunctionPointer pfn = priv->resolve("qt_plugin_query_metadata");
- if (!pfn)
- return false;
+ QPluginMetaData metaData;
+ QFunctionPointer pfn = priv->resolve("qt_plugin_query_metadata_v2");
+ if (pfn) {
+ metaData = reinterpret_cast<QPluginMetaData (*)()>(pfn)();
+#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
+ } else if ((pfn = priv->resolve("qt_plugin_query_metadata"))) {
+ metaData = reinterpret_cast<QPluginMetaData (*)()>(pfn)();
+ if (metaData.size < sizeof(QPluginMetaData::MagicHeader))
+ return error(QLibrary::tr("metadata too small"));
+
+ // adjust the meta data to point to the header
+ auto data = reinterpret_cast<const char *>(metaData.data);
+ data += sizeof(QPluginMetaData::MagicString);
+ metaData.data = data;
+ metaData.size -= sizeof(QPluginMetaData::MagicString);
+#endif
+ } else {
+ return error(QLibrary::tr("entrypoint to query the plugin meta data not found"));
+ }
- auto metaData = getMetaData(pfn);
- QJsonDocument doc = qJsonFromRawLibraryMetaData(reinterpret_cast<const char *>(metaData.data), metaData.size, errMsg);
- if (doc.isNull())
- return false;
- priv->metaData = doc.object();
- return true;
+ if (metaData.size < sizeof(QPluginMetaData::Header))
+ return error(QLibrary::tr("metadata too small"));
+
+ if (priv->metaData.parse(metaData))
+ return true;
+ *errMsg = priv->metaData.errorString();
+ return false;
}
bool QLibraryPrivate::isPlugin()
@@ -763,8 +726,8 @@ void QLibraryPrivate::updatePluginState()
bool success = false;
-#if defined(Q_OS_UNIX) && !defined(Q_OS_MAC)
- if (fileName.endsWith(QLatin1String(".debug"))) {
+#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
// the problem is that they are valid shared library files
@@ -779,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.
@@ -799,28 +777,24 @@ void QLibraryPrivate::updatePluginState()
pluginState = IsNotAPlugin; // be pessimistic
- uint qt_version = (uint)metaData.value(QLatin1String("version")).toDouble();
- bool debug = metaData.value(QLatin1String("debug")).toBool();
+ 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)) {
- if (qt_debug_component()) {
- qWarning("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,
debug ? "debug" : "release");
- }
errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library. (%2.%3.%4) [%5]")
- .arg(fileName)
- .arg((qt_version&0xff0000) >> 16)
- .arg((qt_version&0xff00) >> 8)
- .arg(qt_version&0xff)
- .arg(debug ? QLatin1String("debug") : QLatin1String("release"));
-#ifndef QT_NO_DEBUG_PLUGIN_CHECK
- } else if (debug != QLIBRARY_AS_DEBUG) {
+ .arg(fileName,
+ QString::number((qt_version & 0xff0000) >> 16),
+ QString::number((qt_version & 0xff00) >> 8),
+ QString::number(qt_version & 0xff),
+ debug ? "debug"_L1 : "release"_L1);
+ } else if (PluginMustMatchQtDebug && debug != QtBuildIsDebug) {
//don't issue a qWarning since we will hopefully find a non-debug? --Sam
errorString = QLibrary::tr("The plugin '%1' uses incompatible Qt library."
" (Cannot mix debug and release libraries.)").arg(fileName);
-#endif
} else {
pluginState = IsAPlugin;
}
@@ -842,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;
}
/*!
@@ -858,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()
*/
@@ -872,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;
}
@@ -972,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
@@ -1001,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());
}
/*!
@@ -1025,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
}
/*!
@@ -1162,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.h b/src/corelib/plugin/qlibrary.h
index ac59f45789..f31047b214 100644
--- a/src/corelib/plugin/qlibrary.h
+++ b/src/corelib/plugin/qlibrary.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QLIBRARY_H
#define QLIBRARY_H
@@ -63,7 +27,7 @@ public:
DeepBindHint = 0x10
};
Q_DECLARE_FLAGS(LoadHints, LoadHint)
- Q_FLAG(LoadHint)
+ Q_ENUM(LoadHint)
Q_FLAG(LoadHints)
explicit QLibrary(QObject *parent = nullptr);
diff --git a/src/corelib/plugin/qlibrary_p.h b/src/corelib/plugin/qlibrary_p.h
index 3ca544b2de..87d36ee5c8 100644
--- a/src/corelib/plugin/qlibrary_p.h
+++ b/src/corelib/plugin/qlibrary_p.h
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2021 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QLIBRARY_P_H
#define QLIBRARY_P_H
@@ -52,21 +16,34 @@
// We mean it.
//
-#include <QtCore/private/qglobal_p.h>
#include "QtCore/qlibrary.h"
+
+#include "QtCore/private/qfactoryloader_p.h"
+#include "QtCore/qloggingcategory.h"
#include "QtCore/qmutex.h"
+#include "QtCore/qplugin.h"
#include "QtCore/qpointer.h"
#include "QtCore/qstringlist.h"
-#include "QtCore/qplugin.h"
#ifdef Q_OS_WIN
# include "QtCore/qt_windows.h"
#endif
+#include <memory>
+
QT_REQUIRE_CONFIG(library);
QT_BEGIN_NAMESPACE
-bool qt_debug_component();
+Q_DECLARE_LOGGING_CATEGORY(qt_lcDebugPlugins)
+
+struct QLibraryScanResult
+{
+ qsizetype pos;
+ qsizetype length;
+#if defined(Q_OF_MACH_O)
+ bool isEncrypted = false;
+#endif
+};
class QLibraryStore;
class QLibraryPrivate
@@ -79,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;
@@ -104,7 +87,7 @@ public:
// the mutex protects the fields below
QMutex mutex;
QPointer<QObject> inst; // used by QFactoryLoader
- QJsonObject metaData;
+ QPluginParsedMetaData metaData;
QString errorString;
QString qualifiedFileName;
diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp
index 05b2c92284..a6fb5403cd 100644
--- a/src/corelib/plugin/qlibrary_unix.cpp
+++ b/src/corelib/plugin/qlibrary_unix.cpp
@@ -1,54 +1,18 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2020 Intel Corporation
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2020 Intel Corporation
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformdefs.h"
+#include <qcoreapplication.h>
#include <qfile.h>
#include "qlibrary_p.h"
-#include <qcoreapplication.h>
#include <private/qfilesystementry_p.h>
#include <private/qsimd_p.h>
#include <dlfcn.h>
-#ifdef Q_OS_MAC
+#ifdef Q_OS_DARWIN
# include <private/qcore_mac_p.h>
#endif
@@ -59,11 +23,7 @@
QT_BEGIN_NAMESPACE
-static QString qdlerror()
-{
- const char *err = dlerror();
- return err ? QLatin1Char('(') + QString::fromLocal8Bit(err) + QLatin1Char(')') : QString();
-}
+using namespace Qt::StringLiterals;
QStringList QLibraryPrivate::suffixes_sys(const QString &fullVersion)
{
@@ -82,36 +42,36 @@ QStringList QLibraryPrivate::suffixes_sys(const QString &fullVersion)
// .so is preferred.
# if defined(__ia64)
if (!fullVersion.isEmpty()) {
- suffixes << QLatin1String(".so.%1").arg(fullVersion);
+ suffixes << ".so.%1"_L1.arg(fullVersion);
} else {
- suffixes << QLatin1String(".so");
+ suffixes << ".so"_L1;
}
# endif
if (!fullVersion.isEmpty()) {
- suffixes << QLatin1String(".sl.%1").arg(fullVersion);
- suffixes << QLatin1String(".%1").arg(fullVersion);
+ suffixes << ".sl.%1"_L1.arg(fullVersion);
+ suffixes << ".%1"_L1.arg(fullVersion);
} else {
- suffixes << QLatin1String(".sl");
+ suffixes << ".sl"_L1;
}
#elif defined(Q_OS_AIX)
suffixes << ".a";
#else
if (!fullVersion.isEmpty()) {
- suffixes << QLatin1String(".so.%1").arg(fullVersion);
+ suffixes << ".so.%1"_L1.arg(fullVersion);
} else {
- suffixes << QLatin1String(".so");
+ suffixes << ".so"_L1;
# ifdef Q_OS_ANDROID
suffixes << QStringLiteral(LIBS_SUFFIX);
# endif
}
#endif
-# ifdef Q_OS_MAC
+# ifdef Q_OS_DARWIN
if (!fullVersion.isEmpty()) {
- suffixes << QLatin1String(".%1.bundle").arg(fullVersion);
- suffixes << QLatin1String(".%1.dylib").arg(fullVersion);
+ suffixes << ".%1.bundle"_L1.arg(fullVersion);
+ suffixes << ".%1.dylib"_L1.arg(fullVersion);
} else {
- suffixes << QLatin1String(".bundle") << QLatin1String(".dylib");
+ suffixes << ".bundle"_L1 << ".dylib"_L1;
}
#endif
return suffixes;
@@ -119,21 +79,26 @@ QStringList QLibraryPrivate::suffixes_sys(const QString &fullVersion)
QStringList QLibraryPrivate::prefixes_sys()
{
- return QStringList() << QLatin1String("lib");
+ return QStringList() << "lib"_L1;
}
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);
QString path = fsEntry.path();
QString name = fsEntry.fileName();
- if (path == QLatin1String(".") && !fileName.startsWith(path))
+ if (path == "."_L1 && !fileName.startsWith(path))
path.clear();
else
- path += QLatin1Char('/');
+ path += u'/';
QStringList suffixes;
QStringList prefixes;
@@ -198,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);
@@ -207,10 +172,10 @@ bool QLibraryPrivate::load_sys()
};
if (pluginState == IsAPlugin) {
// add ".avx2" to each suffix in the list
- transform(suffixes, [](QString *s) { s->append(QLatin1String(".avx2")); });
+ transform(suffixes, [](QString *s) { s->append(".avx2"_L1); });
} else {
// prepend "haswell/" to each prefix in the list
- transform(prefixes, [](QString *s) { s->prepend(QLatin1String("haswell/")); });
+ transform(prefixes, [](QString *s) { s->prepend("haswell/"_L1); });
}
}
#endif
@@ -222,15 +187,15 @@ bool QLibraryPrivate::load_sys()
for (int suffix = 0; retry && !hnd && suffix < suffixes.size(); suffix++) {
if (!prefixes.at(prefix).isEmpty() && name.startsWith(prefixes.at(prefix)))
continue;
- if (path.isEmpty() && prefixes.at(prefix).contains(QLatin1Char('/')))
+ if (path.isEmpty() && prefixes.at(prefix).contains(u'/'))
continue;
if (!suffixes.at(suffix).isEmpty() && name.endsWith(suffixes.at(suffix)))
continue;
if (loadHints & QLibrary::LoadArchiveMemberHint) {
attempt = name;
- int lparen = attempt.indexOf(QLatin1Char('('));
+ qsizetype lparen = attempt.indexOf(u'(');
if (lparen == -1)
- lparen = attempt.count();
+ lparen = attempt.size();
attempt = path + prefixes.at(prefix) + attempt.insert(lparen, suffixes.at(suffix));
} else {
attempt = path + prefixes.at(prefix) + name + suffixes.at(suffix);
@@ -240,19 +205,11 @@ bool QLibraryPrivate::load_sys()
#ifdef Q_OS_ANDROID
if (!hnd) {
auto attemptFromBundle = attempt;
- hnd = dlopen(QFile::encodeName(attemptFromBundle.replace(QLatin1Char('/'), QLatin1Char('_'))), 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;
- }
+ hnd = dlopen(QFile::encodeName(attemptFromBundle.replace(u'/', u'_')), dlFlags);
}
#endif
- if (!hnd && fileName.startsWith(QLatin1Char('/')) && QFile::exists(attempt)) {
+ if (!hnd && fileName.startsWith(u'/') && QFile::exists(attempt)) {
// We only want to continue if dlopen failed due to that the shared library did not exist.
// However, we are only able to apply this check for absolute filenames (since they are
// not influenced by the content of LD_LIBRARY_PATH, /etc/ld.so.cache, DT_RPATH etc...)
@@ -262,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);
@@ -279,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;
@@ -291,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;
- errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName,
- QLatin1String(error));
-#else
- errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName, qdlerror());
#endif
+ errorString = QLibrary::tr("Cannot unload library %1: %2").arg(fileName,
+ QLatin1StringView(error));
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 07f5a4b744..c95118e554 100644
--- a/src/corelib/plugin/qlibrary_win.cpp
+++ b/src/corelib/plugin/qlibrary_win.cpp
@@ -1,46 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qplatformdefs.h"
#include "qlibrary_p.h"
-#include "qfile.h"
+
#include "qdir.h"
+#include "qfile.h"
#include "qfileinfo.h"
#include <private/qfilesystementry_p.h>
@@ -48,6 +13,8 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
extern QString qt_error_string(int code);
QStringList QLibraryPrivate::suffixes_sys(const QString& fullVersion)
@@ -80,7 +47,7 @@ bool QLibraryPrivate::load_sys()
QStringList attempts;
if (pluginState != IsAPlugin)
- attempts.append(fileName + QLatin1String(".dll"));
+ attempts.append(fileName + ".dll"_L1);
// If the fileName is an absolute path we try that first, otherwise we
// use the system-specific suffix first
@@ -92,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
@@ -114,9 +81,9 @@ bool QLibraryPrivate::load_sys()
::GetModuleFileName(hnd, buffer, MAX_PATH);
QString moduleFileName = QString::fromWCharArray(buffer);
- moduleFileName.remove(0, 1 + moduleFileName.lastIndexOf(QLatin1Char('\\')));
+ moduleFileName.remove(0, 1 + moduleFileName.lastIndexOf(u'\\'));
const QDir dir(fsEntry.path());
- if (dir.path() == QLatin1String("."))
+ if (dir.path() == "."_L1)
qualifiedFileName = moduleFileName;
else
qualifiedFileName = dir.filePath(moduleFileName);
diff --git a/src/corelib/plugin/qmachparser.cpp b/src/corelib/plugin/qmachparser.cpp
index 9d0b4de4f5..7a82b84cb3 100644
--- a/src/corelib/plugin/qmachparser.cpp
+++ b/src/corelib/plugin/qmachparser.cpp
@@ -1,54 +1,21 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qmachparser_p.h"
-#if defined(Q_OF_MACH_O)
-
#include <qendian.h>
-#include "qlibrary_p.h"
#include <mach-o/loader.h>
#include <mach-o/fat.h>
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
+// Whether we include some extra validity checks
+// (checks to ensure we don't read out-of-bounds are always included)
+static constexpr bool IncludeValidityChecks = true;
+
#if defined(Q_PROCESSOR_X86_64)
# define MACHO64
static const cpu_type_t my_cputype = CPU_TYPE_X86_64;
@@ -81,15 +48,32 @@ typedef section my_section;
static const uint32_t my_magic = MH_MAGIC;
#endif
-static int ns(const QString &reason, const QString &library, QString *errorString)
+Q_DECL_COLD_FUNCTION
+static QLibraryScanResult notfound(const QString &reason, QString *errorString)
{
- if (errorString)
- *errorString = QLibrary::tr("'%1' is not a valid Mach-O binary (%2)")
- .arg(library, reason.isEmpty() ? QLibrary::tr("file is corrupt") : reason);
- return QMachOParser::NotSuitable;
+ *errorString = QLibrary::tr("'%1' is not a valid Mach-O binary (%2)")
+ .arg(*errorString, reason.isEmpty() ? QLibrary::tr("file is corrupt") : reason);
+ 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;
}
-int QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QString *errorString, qsizetype *pos, qsizetype *sectionlen)
+QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *errorString)
{
// The minimum size of a Mach-O binary we're interested in.
// It must have a full Mach header, at least one segment and at least one
@@ -100,7 +84,7 @@ int QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QS
static const size_t MinFatHeaderSize = sizeof(fat_header) + 2 * sizeof(fat_arch);
if (Q_UNLIKELY(fdlen < MinFileSize))
- return ns(QLibrary::tr("file too small"), library, errorString);
+ return notfound(QLibrary::tr("file too small"), errorString);
// find out if this is a fat Mach-O binary first
const my_mach_header *header = nullptr;
@@ -109,12 +93,12 @@ int QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QS
// find our architecture in the binary
const fat_arch *arch = reinterpret_cast<const fat_arch *>(fat + 1);
if (Q_UNLIKELY(fdlen < MinFatHeaderSize)) {
- return ns(QLibrary::tr("file too small"), library, errorString);
+ return notfound(QLibrary::tr("file too small"), errorString);
}
int count = qFromBigEndian(fat->nfat_arch);
if (Q_UNLIKELY(fdlen < sizeof(*fat) + sizeof(*arch) * count))
- return ns(QString(), library, errorString);
+ return notfound(QString(), errorString);
for (int i = 0; i < count; ++i) {
if (arch[i].cputype == qToBigEndian(my_cputype)) {
@@ -123,7 +107,7 @@ int QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QS
uint32_t offset = qFromBigEndian(arch[i].offset);
if (Q_UNLIKELY(size > fdlen) || Q_UNLIKELY(offset > fdlen)
|| Q_UNLIKELY(size + offset > fdlen) || Q_UNLIKELY(size < MinFileSize))
- return ns(QString(), library, errorString);
+ return notfound(QString(), errorString);
header = reinterpret_cast<const my_mach_header *>(m_s + offset);
fdlen = size;
@@ -131,36 +115,35 @@ int QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QS
}
}
if (!header)
- return ns(QLibrary::tr("no suitable architecture in fat binary"), library, errorString);
+ return notfound(QLibrary::tr("no suitable architecture in fat binary"), errorString);
// check the magic again
if (Q_UNLIKELY(header->magic != my_magic))
- return ns(QString(), library, errorString);
+ return notfound(QString(), errorString);
} else {
header = reinterpret_cast<const my_mach_header *>(m_s);
fat = 0;
// check magic
if (header->magic != my_magic)
- return ns(QLibrary::tr("invalid magic %1").arg(qFromBigEndian(header->magic), 8, 16, QLatin1Char('0')),
- library, errorString);
+ return notfound(QLibrary::tr("invalid magic %1").arg(qFromBigEndian(header->magic),
+ 8, 16, '0'_L1),
+ errorString);
}
- // from this point on, fdlen is specific to this architecture
// from this point on, everything is in host byte order
- *pos = reinterpret_cast<const char *>(header) - m_s;
// (re-)check the CPU type
// ### should we check the CPU subtype? Maybe on ARM?
if (header->cputype != my_cputype) {
if (fat)
- return ns(QString(), library, errorString);
- return ns(QLibrary::tr("wrong architecture"), library, errorString);
+ return notfound(QString(), errorString);
+ return notfound(QLibrary::tr("wrong architecture"), errorString);
}
// check the file type
if (Q_UNLIKELY(header->filetype != MH_BUNDLE && header->filetype != MH_DYLIB))
- return ns(QLibrary::tr("not a dynamic library"), library, errorString);
+ return notfound(QLibrary::tr("not a dynamic library"), errorString);
// find the __TEXT segment, "qtmetadata" section
const my_segment_command *seg = reinterpret_cast<const my_segment_command *>(header + 1);
@@ -171,14 +154,14 @@ int QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QS
// We're sure that the file size includes at least one load command
// but we have to check anyway if we're past the first
if (Q_UNLIKELY(fdlen < minsize + sizeof(load_command)))
- return ns(QString(), library, errorString);
+ return notfound(QString(), errorString);
// cmdsize can't be trusted until validated
// so check it against fdlen anyway
// (these are unsigned operations, with overflow behavior specified in the standard)
minsize += seg->cmdsize;
if (Q_UNLIKELY(fdlen < minsize) || Q_UNLIKELY(fdlen < seg->cmdsize))
- return ns(QString(), library, errorString);
+ return notfound(QString(), errorString);
const uint32_t MyLoadCommand = sizeof(void *) > 4 ? LC_SEGMENT_64 : LC_SEGMENT;
if (seg->cmd != MyLoadCommand)
@@ -195,11 +178,25 @@ int QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QS
// found it!
if (Q_UNLIKELY(fdlen < sect[j].offset) || Q_UNLIKELY(fdlen < sect[j].size)
|| Q_UNLIKELY(fdlen < sect[j].offset + sect[j].size))
- return ns(QString(), library, errorString);
+ return notfound(QString(), errorString);
+
+ 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;
- *pos += sect[j].offset;
- *sectionlen = sect[j].size;
- return QtMetaDataSection;
+ // 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)
+ return notfound(QLibrary::tr(".qtmetadata section has incorrect magic"), errorString);
+ }
+
+ pos += sizeof(QPluginMetaData::MagicString);
+ return { pos, qsizetype(sect[j].size - sizeof(QPluginMetaData::MagicString)), binaryIsEncrypted };
}
}
@@ -207,13 +204,9 @@ int QMachOParser::parse(const char *m_s, ulong fdlen, const QString &library, QS
seg = reinterpret_cast<const my_segment_command *>(reinterpret_cast<const char *>(seg) + seg->cmdsize);
}
-// // No Qt section was found, but at least we know that where the proper architecture's boundaries are
-// return NoQtSection;
- if (errorString)
- *errorString = QLibrary::tr("'%1' is not a Qt plugin").arg(library);
- return NotSuitable;
+ // No .qtmetadata section was found
+ *errorString = QLibrary::tr("'%1' is not a Qt plugin").arg(*errorString);
+ return {};
}
QT_END_NAMESPACE
-
-#endif
diff --git a/src/corelib/plugin/qmachparser_p.h b/src/corelib/plugin/qmachparser_p.h
index 290b68876f..85a174e66e 100644
--- a/src/corelib/plugin/qmachparser_p.h
+++ b/src/corelib/plugin/qmachparser_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QMACHPARSER_P_H
#define QMACHPARSER_P_H
@@ -51,8 +15,7 @@
// We mean it.
//
-#include <qendian.h>
-#include <private/qglobal_p.h>
+#include "qlibrary_p.h"
QT_REQUIRE_CONFIG(library);
@@ -66,12 +29,11 @@ class QLibraryPrivate;
class Q_AUTOTEST_EXPORT QMachOParser
{
public:
- enum { QtMetaDataSection, NoQtSection, NotSuitable };
- static int parse(const char *m_s, ulong fdlen, const QString &library, QString *errorString, qsizetype *pos, qsizetype *sectionlen);
+ static QLibraryScanResult parse(const char *m_s, ulong fdlen, QString *errorString);
};
QT_END_NAMESPACE
-#endif // defined(Q_OF_ELF) && defined(Q_CC_GNU)
+#endif // defined(Q_OF_MACH_O)
#endif // QMACHPARSER_P_H
diff --git a/src/corelib/plugin/qplugin.h b/src/corelib/plugin/qplugin.h
index 411edba792..909c8acdcc 100644
--- a/src/corelib/plugin/qplugin.h
+++ b/src/corelib/plugin/qplugin.h
@@ -1,52 +1,23 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Copyright (C) 2021 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2021 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPLUGIN_H
#define QPLUGIN_H
+#if 0
+#pragma qt_class(QtPlugin)
+#endif
+
#include <QtCore/qobject.h>
#include <QtCore/qpointer.h>
#include <QtCore/qjsonobject.h>
+#include <QtCore/q20algorithm.h>
+
QT_BEGIN_NAMESPACE
+// Used up to Qt 6.2
inline constexpr unsigned char qPluginArchRequirements()
{
return 0
@@ -65,7 +36,71 @@ inline constexpr unsigned char qPluginArchRequirements()
typedef QObject *(*QtPluginInstanceFunction)();
struct QPluginMetaData
{
- const uchar *data;
+ static constexpr quint8 CurrentMetaDataVersion = 1;
+ static constexpr char MagicString[] = {
+ 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!'
+ };
+
+ template <size_t OSize, typename OO, size_t ISize, typename II>
+ static constexpr void copy(OO (&out)[OSize], II (&in)[ISize])
+ {
+ static_assert(OSize <= ISize, "Output would not be fully initialized");
+ q20::copy_n(in, OSize, out);
+ }
+
+ static constexpr quint8 archRequirements()
+ {
+ quint8 v = 0;
+#if defined(__AVX512F__)
+ v = 4; // x86-64-v4: AVX512F, AVX512BW, AVX512CD, AVX512DQ and AVX512VL
+#elif defined(__AVX__) || defined(__BMI__) || defined(__BMI2__) || defined(__MOVBE__)
+ v = 3; // x86-64-v3: AVX, AVX2, BMI1, BMI2, F16C, FMA, LZCNT, MOVBE, XSAVE
+#elif defined(__SSE3__)
+ v = 2; // x86-64-v2: POPCNT, SSE3, SSSE3, SSE4.1 and SSE4.2.
+#elif defined(__SSE__) || defined(__MMX___)
+ v = 1; // x86-64 baseline: SSE and SSE2
+#endif
+#ifndef QT_NO_DEBUG
+ v |= 0x80;
+#endif
+ return v;
+ }
+
+ struct Header {
+ quint8 version = CurrentMetaDataVersion;
+ quint8 qt_major_version = QT_VERSION_MAJOR;
+ quint8 qt_minor_version = QT_VERSION_MINOR;
+ quint8 plugin_arch_requirements = archRequirements();
+ };
+ static_assert(alignof(Header) == 1, "Alignment of header incorrect with this compiler");
+
+ struct MagicHeader {
+ char magic[sizeof(QPluginMetaData::MagicString)] = {};
+ constexpr MagicHeader() { copy(magic, QPluginMetaData::MagicString); }
+ Header header = {};
+ };
+ static_assert(alignof(MagicHeader) == 1, "Alignment of header incorrect with this compiler");
+
+ struct ElfNoteHeader {
+ static constexpr quint32 NoteType = 0x74510001;
+ static constexpr char NoteName[] = "qt-project!";
+
+ // ELF note header
+ quint32 n_namesz = sizeof(name);
+ quint32 n_descsz;
+ quint32 n_type = NoteType;
+ char name[sizeof(NoteName)] = {};
+
+ // payload
+ alignas(void *) // mandatory alignment as per ELF note requirements
+ Header header = {};
+ constexpr ElfNoteHeader(quint32 payloadSize) : n_descsz(sizeof(header) + payloadSize)
+ { QPluginMetaData::copy(name, NoteName); }
+ };
+ static_assert(alignof(ElfNoteHeader) == alignof(void*), "Alignment of header incorrect with this compiler");
+ static_assert((sizeof(ElfNoteHeader::name) % 4) == 0, "ELF note name length not a multiple of 4");
+
+ const void *data;
size_t size;
};
typedef QPluginMetaData (*QtPluginMetaDataFunction)();
@@ -83,6 +118,7 @@ public:
private:
qsizetype rawMetaDataSize;
const void *rawMetaData;
+ friend class QFactoryLoader;
};
Q_DECLARE_TYPEINFO(QStaticPlugin, Q_PRIMITIVE_TYPE);
@@ -91,8 +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)
-// TODO: Implement section parsing on Mac
+#elif defined(Q_OS_DARWIN)
# define QT_PLUGIN_METADATA_SECTION \
__attribute__ ((section ("__TEXT,qtmetadata"))) __attribute__((used))
#elif defined(Q_CC_MSVC)
@@ -104,6 +139,57 @@ void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin staticPlugin);
# define QT_PLUGIN_METADATA_SECTION
#endif
+// Since Qt 6.3
+template <auto (&PluginMetaData)> class QPluginMetaDataV2
+{
+ struct ElfNotePayload : QPluginMetaData::ElfNoteHeader {
+ static constexpr size_t HeaderOffset = offsetof(QPluginMetaData::ElfNoteHeader, header);
+ quint8 payload[sizeof(PluginMetaData)] = {};
+ constexpr ElfNotePayload() : ElfNoteHeader(sizeof(PluginMetaData))
+ { QPluginMetaData::copy(payload, PluginMetaData); }
+ };
+
+ struct RegularPayload : QPluginMetaData::MagicHeader {
+ static constexpr size_t HeaderOffset = offsetof(QPluginMetaData::MagicHeader, header);
+ quint8 payload[sizeof(PluginMetaData)] = {};
+ constexpr RegularPayload() { QPluginMetaData::copy(payload, PluginMetaData); }
+ };
+
+ struct StaticPayload {
+ static constexpr size_t HeaderOffset = 0;
+ QPluginMetaData::Header header = {};
+ quint8 payload[sizeof(PluginMetaData)] = {};
+ constexpr StaticPayload() { QPluginMetaData::copy(payload, PluginMetaData); }
+ };
+
+#if defined(QT_STATICPLUGIN)
+# define QT_PLUGIN_METADATAV2_SECTION
+ using Payload = StaticPayload;
+#elif defined(Q_OF_ELF)
+# ifdef Q_CC_CLANG
+# define QT_PLUGIN_METADATAV2_SECTION \
+ __attribute__((section(".note.qt.metadata"), used, aligned(alignof(void *)), \
+ no_sanitize("address")))
+# else
+# define QT_PLUGIN_METADATAV2_SECTION \
+ __attribute__((section(".note.qt.metadata"), used, aligned(alignof(void *))))
+# endif
+ using Payload = ElfNotePayload;
+#else
+# define QT_PLUGIN_METADATAV2_SECTION QT_PLUGIN_METADATA_SECTION
+ using Payload = RegularPayload;
+#endif
+
+ Payload payload = {};
+
+public:
+ operator QPluginMetaData() const
+ {
+ Q_ASSERT(reinterpret_cast<const char *>(&payload) + Payload::HeaderOffset ==
+ reinterpret_cast<const char *>(&payload.header));
+ return { &payload.header, sizeof(payload) - Payload::HeaderOffset };
+ }
+};
#define Q_IMPORT_PLUGIN(PLUGIN) \
extern const QT_PREPEND_NAMESPACE(QStaticPlugin) qt_static_plugin_##PLUGIN(); \
@@ -134,25 +220,38 @@ void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin staticPlugin);
}
#if defined(QT_STATICPLUGIN)
+# define QT_MOC_EXPORT_PLUGIN_COMMON(PLUGINCLASS, MANGLEDNAME) \
+ static QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance_##MANGLEDNAME() \
+ Q_PLUGIN_INSTANCE(PLUGINCLASS) \
+ const QT_PREPEND_NAMESPACE(QStaticPlugin) qt_static_plugin_##MANGLEDNAME() \
+ { return { qt_plugin_instance_##MANGLEDNAME, qt_plugin_query_metadata_##MANGLEDNAME}; } \
+ /**/
# define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME) \
- static QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance_##PLUGINCLASSNAME() \
- Q_PLUGIN_INSTANCE(PLUGINCLASS) \
static QPluginMetaData qt_plugin_query_metadata_##PLUGINCLASSNAME() \
{ return { qt_pluginMetaData_##PLUGINCLASSNAME, sizeof qt_pluginMetaData_##PLUGINCLASSNAME }; } \
- const QT_PREPEND_NAMESPACE(QStaticPlugin) qt_static_plugin_##PLUGINCLASSNAME() { \
- return { qt_plugin_instance_##PLUGINCLASSNAME, qt_plugin_query_metadata_##PLUGINCLASSNAME}; \
- }
+ QT_MOC_EXPORT_PLUGIN_COMMON(PLUGINCLASS, PLUGINCLASSNAME)
+# define QT_MOC_EXPORT_PLUGIN_V2(PLUGINCLASS, MANGLEDNAME, MD) \
+ static QT_PREPEND_NAMESPACE(QPluginMetaData) qt_plugin_query_metadata_##MANGLEDNAME() \
+ { static constexpr QPluginMetaDataV2<MD> md{}; return md; } \
+ QT_MOC_EXPORT_PLUGIN_COMMON(PLUGINCLASS, MANGLEDNAME)
#else
+# define QT_MOC_EXPORT_PLUGIN_COMMON(PLUGINCLASS, MANGLEDNAME) \
+ extern "C" Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance() \
+ Q_PLUGIN_INSTANCE(PLUGINCLASS) \
+ /**/
# define QT_MOC_EXPORT_PLUGIN(PLUGINCLASS, PLUGINCLASSNAME) \
extern "C" Q_DECL_EXPORT \
QPluginMetaData qt_plugin_query_metadata() \
{ return { qt_pluginMetaData_##PLUGINCLASSNAME, sizeof qt_pluginMetaData_##PLUGINCLASSNAME }; } \
- extern "C" Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QObject) *qt_plugin_instance() \
- Q_PLUGIN_INSTANCE(PLUGINCLASS)
+ QT_MOC_EXPORT_PLUGIN_COMMON(PLUGINCLASS, PLUGINCLASSNAME)
+# define QT_MOC_EXPORT_PLUGIN_V2(PLUGINCLASS, MANGLEDNAME, MD) \
+ extern "C" Q_DECL_EXPORT QT_PREPEND_NAMESPACE(QPluginMetaData) qt_plugin_query_metadata_v2()\
+ { static constexpr QT_PLUGIN_METADATAV2_SECTION QPluginMetaDataV2<MD> md{}; return md; } \
+ QT_MOC_EXPORT_PLUGIN_COMMON(PLUGINCLASS, MANGLEDNAME)
#endif
#define Q_EXPORT_PLUGIN(PLUGIN) \
diff --git a/src/corelib/plugin/qplugin.qdoc b/src/corelib/plugin/qplugin.qdoc
index 968fb4820a..0ca248a548 100644
--- a/src/corelib/plugin/qplugin.qdoc
+++ b/src/corelib/plugin/qplugin.qdoc
@@ -1,32 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\headerfile <QtPlugin>
+ \inmodule QtCore
\title Defining Plugins
\keyword qtplugin-defining-plugins
\ingroup plugins
@@ -42,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
@@ -76,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.
@@ -105,11 +77,9 @@
\snippet code/doc_src_qplugin.cpp 2
Static plugins must also be included by the linker when your
- application is built. For Qt's predefined plugins,
- you can use the \c QTPLUGIN to add
- the required plugins to your build. For example:
+ application is built. See \l{Static Plugins} for more information
+ on this.
- \snippet code/doc_src_qplugin.pro 3
- \sa {Static Plugins}, {How to Create Qt Plugins}, {qmake-getting-started}{Getting Started with qmake}
+ \sa {Static Plugins}, {How to Create Qt Plugins}
*/
diff --git a/src/corelib/plugin/qplugin_p.h b/src/corelib/plugin/qplugin_p.h
index ce45ebf700..534ca569df 100644
--- a/src/corelib/plugin/qplugin_p.h
+++ b/src/corelib/plugin/qplugin_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPLUGIN_P_H
#define QPLUGIN_P_H
@@ -61,7 +25,8 @@ enum class QtPluginMetaDataKeys {
IID,
ClassName,
MetaData,
- URI
+ URI,
+ IsDebug,
};
// F(IntKey, StringKey, Description)
@@ -70,7 +35,63 @@ enum class QtPluginMetaDataKeys {
F(QtPluginMetaDataKeys::IID, "IID", "Plugin's Interface ID") \
F(QtPluginMetaDataKeys::ClassName, "className", "Plugin class name") \
F(QtPluginMetaDataKeys::MetaData, "MetaData", "Other meta data") \
- F(QtPluginMetaDataKeys::URI, "URI", "Plugin URI")
+ F(QtPluginMetaDataKeys::URI, "URI", "Plugin URI") \
+ /* not output by moc in CBOR */ \
+ F(QtPluginMetaDataKeys::QtVersion, "version", "Qt version") \
+ F(QtPluginMetaDataKeys::Requirements, "archlevel", "Architectural level") \
+ F(QtPluginMetaDataKeys::IsDebug, "debug", "Debug-mode plugin") \
+ /**/
+
+namespace {
+struct DecodedArchRequirements
+{
+ quint8 level;
+ bool isDebug;
+ friend constexpr bool operator==(DecodedArchRequirements r1, DecodedArchRequirements r2)
+ {
+ return r1.level == r2.level && r1.isDebug == r2.isDebug;
+ }
+};
+
+static constexpr DecodedArchRequirements decodeVersion0ArchRequirements(quint8 value)
+{
+ // see qPluginArchRequirements() and QPluginMetaDataV2::archRequirements()
+ DecodedArchRequirements r = {};
+#ifdef Q_PROCESSOR_X86
+ if (value & 4)
+ r.level = 4; // AVX512F -> x86-64-v4
+ else if (value & 2)
+ r.level = 3; // AVX2 -> x86-64-v3
+#endif
+ if (value & 1)
+ r.isDebug = true;
+ return r;
+}
+// self checks
+static_assert(decodeVersion0ArchRequirements(0) == DecodedArchRequirements{ 0, false });
+static_assert(decodeVersion0ArchRequirements(1) == DecodedArchRequirements{ 0, true });
+#ifdef Q_PROCESSOR_X86
+static_assert(decodeVersion0ArchRequirements(2) == DecodedArchRequirements{ 3, false });
+static_assert(decodeVersion0ArchRequirements(3) == DecodedArchRequirements{ 3, true });
+static_assert(decodeVersion0ArchRequirements(4) == DecodedArchRequirements{ 4, false });
+static_assert(decodeVersion0ArchRequirements(5) == DecodedArchRequirements{ 4, true });
+#endif
+
+static constexpr DecodedArchRequirements decodeVersion1ArchRequirements(quint8 value)
+{
+ return { quint8(value & 0x7f), bool(value & 0x80) };
+}
+// self checks
+static_assert(decodeVersion1ArchRequirements(0) == DecodedArchRequirements{ 0, false });
+static_assert(decodeVersion1ArchRequirements(0x80) == DecodedArchRequirements{ 0, true });
+#ifdef Q_PROCESSOR_X86
+static_assert(decodeVersion1ArchRequirements(1) == DecodedArchRequirements{ 1, false });
+static_assert(decodeVersion1ArchRequirements(3) == DecodedArchRequirements{ 3, false});
+static_assert(decodeVersion1ArchRequirements(4) == DecodedArchRequirements{ 4, false });
+static_assert(decodeVersion1ArchRequirements(0x82) == DecodedArchRequirements{ 2, true });
+static_assert(decodeVersion1ArchRequirements(0x84) == DecodedArchRequirements{ 4, true });
+#endif
+} // unnamed namespace
QT_END_NAMESPACE
diff --git a/src/corelib/plugin/qpluginloader.cpp b/src/corelib/plugin/qpluginloader.cpp
index 131506f73e..03b8cfbb84 100644
--- a/src/corelib/plugin/qpluginloader.cpp
+++ b/src/corelib/plugin/qpluginloader.cpp
@@ -1,55 +1,24 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Copyright (C) 2018 Intel Corporation.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qplatformdefs.h"
-
-#include "qplugin.h"
-#include "qcoreapplication.h"
+// Copyright (C) 2016 The Qt Company Ltd.
+// Copyright (C) 2018 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
#include "qpluginloader.h"
-#include <qfileinfo.h>
-#include "qfactoryloader_p.h"
+
+#include "qcoreapplication.h"
#include "qdebug.h"
#include "qdir.h"
+#include "qfactoryloader_p.h"
+#include "qfileinfo.h"
+#include "qjsondocument.h"
+
+#if QT_CONFIG(library)
+# include "qlibrary_p.h"
+#endif
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
#if QT_CONFIG(library)
/*!
@@ -102,9 +71,11 @@ QT_BEGIN_NAMESPACE
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;
+
/*!
Constructs a plugin loader with the given \a parent.
*/
@@ -128,7 +99,7 @@ QPluginLoader::QPluginLoader(const QString &fileName, QObject *parent)
: QObject(parent), d(nullptr), did_load(false)
{
setFileName(fileName);
- setLoadHints(QLibrary::PreventUnloadHint);
+ setLoadHints(defaultLoadHints);
}
/*!
@@ -186,7 +157,7 @@ QJsonObject QPluginLoader::metaData() const
{
if (!d)
return QJsonObject();
- return d->metaData;
+ return d->metaData.toJson();
}
/*!
@@ -264,12 +235,10 @@ static QString locatePlugin(const QString& fileName)
suffixes.prepend(QString());
// Split up "subdir/filename"
- const int slash = fileName.lastIndexOf(QLatin1Char('/'));
+ 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 '/'
- const bool debug = qt_debug_component();
-
QStringList paths;
if (isAbsolute) {
paths.append(fileName.left(slash)); // don't include the '/'
@@ -277,29 +246,26 @@ 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;
- const QString fn = path + QLatin1String("/lib") + pluginPath.replace(QLatin1Char('/'), QLatin1Char('_'));
- if (debug)
- qDebug() << "Trying..." << fn;
+ const QString fn = path + "/lib"_L1 + pluginPath.replace(u'/', u'_');
+ qCDebug(qt_lcDebugPlugins) << "Trying..." << fn;
if (QFileInfo(fn).isFile())
return fn;
}
#endif
- const QString fn = path + QLatin1Char('/') + basePath + prefix + baseName + suffix;
- if (debug)
- qDebug() << "Trying..." << fn;
+ const QString fn = path + u'/' + basePath + prefix + baseName + suffix;
+ qCDebug(qt_lcDebugPlugins) << "Trying..." << fn;
if (QFileInfo(fn).isFile())
return fn;
}
}
}
- if (debug)
- qDebug() << fileName << "not found";
+ qCDebug(qt_lcDebugPlugins) << fileName << "not found";
return QString();
}
#endif
@@ -329,7 +295,7 @@ static QString locatePlugin(const QString& fileName)
void QPluginLoader::setFileName(const QString &fileName)
{
#if defined(QT_SHARED)
- QLibrary::LoadHints lh = QLibrary::PreventUnloadHint;
+ QLibrary::LoadHints lh = defaultLoadHints;
if (d) {
lh = d->loadHints();
d->release();
@@ -344,11 +310,8 @@ void QPluginLoader::setFileName(const QString &fileName)
d->updatePluginState();
#else
- if (qt_debug_component()) {
- qWarning("Cannot load %s into a statically linked Qt library.",
- (const char *)QFile::encodeName(fileName));
- }
- Q_UNUSED(fileName);
+ qCWarning(qt_lcDebugPlugins, "Cannot load '%ls' into a statically linked Qt library.",
+ qUtf16Printable(fileName));
#endif
}
@@ -386,15 +349,21 @@ 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
{
- return d ? d->loadHints() : QLibrary::LoadHints();
+ // Not having a d-pointer means that the user hasn't called
+ // setLoadHints() / setFileName() yet. In setFileName() we will
+ // then force defaultLoadHints on loading, so we must return them
+ // from here as well.
+
+ return d ? d->loadHints() : defaultLoadHints;
}
#endif // QT_CONFIG(library)
@@ -411,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);
}
/*!
@@ -422,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;
}
@@ -478,13 +458,10 @@ QList<QStaticPlugin> QPluginLoader::staticPlugins()
*/
QJsonObject QStaticPlugin::metaData() const
{
- auto ptr = static_cast<const char *>(rawMetaData);
-
- QString errMsg;
- QJsonDocument doc = qJsonFromRawLibraryMetaData(ptr, rawMetaDataSize, &errMsg);
- Q_ASSERT(doc.isObject());
- Q_ASSERT(errMsg.isEmpty());
- return doc.object();
+ QByteArrayView data(static_cast<const char *>(rawMetaData), rawMetaDataSize);
+ QPluginParsedMetaData parsed(data);
+ Q_ASSERT(!parsed.isError());
+ return parsed.toJson();
}
QT_END_NAMESPACE
diff --git a/src/corelib/plugin/qpluginloader.h b/src/corelib/plugin/qpluginloader.h
index b242de2991..e541994e62 100644
--- a/src/corelib/plugin/qpluginloader.h
+++ b/src/corelib/plugin/qpluginloader.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QPLUGINLOADER_H
#define QPLUGINLOADER_H
diff --git a/src/corelib/plugin/qsystemlibrary.cpp b/src/corelib/plugin/qsystemlibrary.cpp
index 593b0c3be7..d3dff226d9 100644
--- a/src/corelib/plugin/qsystemlibrary.cpp
+++ b/src/corelib/plugin/qsystemlibrary.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qsystemlibrary_p.h"
#include <QtCore/qvarlengtharray.h>
@@ -72,52 +36,56 @@
QT_BEGIN_NAMESPACE
+using namespace Qt::StringLiterals;
+
#if !defined(QT_BOOTSTRAPPED)
extern QString qAppFileName();
#endif
static QString qSystemDirectory()
{
- QVarLengthArray<wchar_t, MAX_PATH> fullPath;
-
- UINT retLen = ::GetSystemDirectory(fullPath.data(), MAX_PATH);
- if (retLen > MAX_PATH) {
- fullPath.resize(retLen);
- retLen = ::GetSystemDirectory(fullPath.data(), retLen);
- }
- // in some rare cases retLen might be 0
- return QString::fromWCharArray(fullPath.constData(), int(retLen));
+ static const QString result = []() -> QString {
+ QVarLengthArray<wchar_t, MAX_PATH> fullPath = {};
+ UINT retLen = ::GetSystemDirectoryW(fullPath.data(), MAX_PATH);
+ if (retLen > MAX_PATH) {
+ fullPath.resize(retLen);
+ retLen = ::GetSystemDirectoryW(fullPath.data(), retLen);
+ }
+ // in some rare cases retLen might be 0
+ return QString::fromWCharArray(fullPath.constData(), int(retLen));
+ }();
+ return result;
}
HINSTANCE QSystemLibrary::load(const wchar_t *libraryName, bool onlySystemDirectory /* = true */)
{
+ if (onlySystemDirectory)
+ return ::LoadLibraryExW(libraryName, nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
+
QStringList searchOrder;
#if !defined(QT_BOOTSTRAPPED)
- if (!onlySystemDirectory)
- searchOrder << QFileInfo(qAppFileName()).path();
+ searchOrder << QFileInfo(qAppFileName()).path();
#endif
searchOrder << qSystemDirectory();
- if (!onlySystemDirectory) {
- const QString PATH(QLatin1String(qgetenv("PATH").constData()));
- searchOrder << PATH.split(QLatin1Char(';'), Qt::SkipEmptyParts);
- }
- QString fileName = QString::fromWCharArray(libraryName);
- fileName.append(QLatin1String(".dll"));
+ const QString PATH(QLatin1StringView(qgetenv("PATH")));
+ searchOrder << PATH.split(u';', Qt::SkipEmptyParts);
+
+ const QString fileName = QString::fromWCharArray(libraryName);
// Start looking in the order specified
for (int i = 0; i < searchOrder.count(); ++i) {
QString fullPathAttempt = searchOrder.at(i);
- if (!fullPathAttempt.endsWith(QLatin1Char('\\'))) {
- fullPathAttempt.append(QLatin1Char('\\'));
+ if (!fullPathAttempt.endsWith(u'\\')) {
+ fullPathAttempt.append(u'\\');
}
fullPathAttempt.append(fileName);
HINSTANCE inst = ::LoadLibrary(reinterpret_cast<const wchar_t *>(fullPathAttempt.utf16()));
- if (inst != 0)
+ if (inst != nullptr)
return inst;
}
- return 0;
+ return nullptr;
}
QT_END_NAMESPACE
diff --git a/src/corelib/plugin/qsystemlibrary_p.h b/src/corelib/plugin/qsystemlibrary_p.h
index b27e746fd3..0b4ad7dbc2 100644
--- a/src/corelib/plugin/qsystemlibrary_p.h
+++ b/src/corelib/plugin/qsystemlibrary_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QSYSTEMLIBRARY_P_H
#define QSYSTEMLIBRARY_P_H
@@ -64,27 +28,23 @@ public:
explicit QSystemLibrary(const QString &libraryName)
{
m_libraryName = libraryName;
- m_handle = 0;
- m_didLoad = false;
}
explicit QSystemLibrary(const wchar_t *libraryName)
{
m_libraryName = QString::fromWCharArray(libraryName);
- m_handle = 0;
- m_didLoad = false;
}
bool load(bool onlySystemDirectory = true)
{
m_handle = load((const wchar_t *)m_libraryName.utf16(), onlySystemDirectory);
m_didLoad = true;
- return (m_handle != 0);
+ return (m_handle != nullptr);
}
bool isLoaded()
{
- return (m_handle != 0);
+ return (m_handle != nullptr);
}
QFunctionPointer resolve(const char *symbol)
@@ -92,7 +52,7 @@ public:
if (!m_didLoad)
load();
if (!m_handle)
- return 0;
+ return nullptr;
return QFunctionPointer(GetProcAddress(m_handle, symbol));
}
@@ -102,10 +62,11 @@ public:
}
static Q_CORE_EXPORT HINSTANCE load(const wchar_t *lpFileName, bool onlySystemDirectory = true);
+
private:
- HINSTANCE m_handle;
- QString m_libraryName;
- bool m_didLoad;
+ HINSTANCE m_handle = nullptr;
+ QString m_libraryName = {};
+ bool m_didLoad = false;
};
QT_END_NAMESPACE
diff --git a/src/corelib/plugin/quuid.cpp b/src/corelib/plugin/quuid.cpp
index 7f7d537fe4..9c7216c3c5 100644
--- a/src/corelib/plugin/quuid.cpp
+++ b/src/corelib/plugin/quuid.cpp
@@ -1,42 +1,6 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2017 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Marc Mutz <marc.mutz@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "quuid.h"
@@ -49,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 };
@@ -323,6 +290,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.
@@ -395,7 +481,7 @@ static QUuid createFromName(const QUuid &ns, const QByteArray &baseData, QCrypto
public data members in QUuid.
\note In Qt versions prior to 6.3, this function was an overload
- set consisting of QStringView and QLatin1String instead of
+ set consisting of QStringView and QLatin1StringView instead of
one function taking QAnyStringView.
\sa toString(), QUuid()
@@ -416,13 +502,13 @@ static QUuid uuidFromString(QStringView text) noexcept
return _q_uuidFromHex(latin1);
}
-static QUuid uuidFromString(QLatin1String text) noexcept
+static QUuid uuidFromString(QLatin1StringView text) noexcept
{
if (Q_UNLIKELY(text.size() < MaxStringUuidLength - 2
- || (text.front() == QLatin1Char('{') && text.size() < MaxStringUuidLength - 1))) {
+ || (text.front() == '{' && text.size() < MaxStringUuidLength - 1))) {
// Too short. Don't call _q_uuidFromHex(); QL1Ss need not be NUL-terminated,
// and we don't want to read trailing garbage as potentially valid data.
- text = QLatin1String();
+ text = QLatin1StringView();
}
return _q_uuidFromHex(text.data());
}
@@ -431,7 +517,7 @@ Q_ALWAYS_INLINE
// can treat UTF-8 the same as Latin-1:
static QUuid uuidFromString(QUtf8StringView text) noexcept
{
- return uuidFromString(QLatin1String(text.data(), text.size()));
+ return uuidFromString(QLatin1StringView(text.data(), text.size()));
}
QUuid QUuid::fromString(QAnyStringView text) noexcept
@@ -504,32 +590,13 @@ 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());
}
/*!
@@ -659,27 +726,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
@@ -689,14 +745,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);
@@ -710,9 +771,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;
}
@@ -931,7 +992,7 @@ QUuid QUuid::createUuid()
return result;
}
-#else // Q_OS_WIN
+#elif !defined(QT_BOOTSTRAPPED)
QUuid QUuid::createUuid()
{
@@ -945,7 +1006,7 @@ QUuid QUuid::createUuid()
return result;
}
-#endif // !Q_OS_WIN
+#endif // !Q_OS_WIN && !QT_BOOTSTRAPPED
/*!
\fn bool QUuid::operator==(const GUID &guid) const
diff --git a/src/corelib/plugin/quuid.h b/src/corelib/plugin/quuid.h
index 9229d5b694..7125e8e2cc 100644
--- a/src/corelib/plugin/quuid.h
+++ b/src/corelib/plugin/quuid.h
@@ -1,48 +1,13 @@
-/****************************************************************************
-**
-** Copyright (C) 2020 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtCore module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QUUID_H
#define QUUID_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
@@ -55,14 +20,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) {}
@@ -91,31 +55,73 @@ 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)} {}
static QUuid fromString(QAnyStringView string) noexcept;
-#if QT_REMOVED_SINCE(6, 3)
+#if QT_CORE_REMOVED_SINCE(6, 3)
explicit QUuid(const QString &);
static QUuid fromString(QStringView string) noexcept;
- static QUuid fromString(QLatin1String string) noexcept;
+ static QUuid fromString(QLatin1StringView string) noexcept;
explicit QUuid(const char *);
explicit QUuid(const QByteArray &);
#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;
-#if QT_REMOVED_SINCE(6, 3)
+
+ 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;
+#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
+
constexpr bool operator==(const QUuid &orig) const noexcept
{
if (data1 != orig.data1 || data2 != orig.data2 ||
@@ -137,7 +143,7 @@ public:
bool operator<(const QUuid &other) const noexcept;
bool operator>(const QUuid &other) const noexcept;
-#if defined(Q_OS_WIN) || defined(Q_CLANG_QDOC)
+#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
@@ -187,7 +193,7 @@ public:
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);
@@ -213,11 +219,92 @@ Q_CORE_EXPORT QDebug operator<<(QDebug, const QUuid &);
Q_CORE_EXPORT size_t qHash(const QUuid &uuid, size_t seed = 0) noexcept;
+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
+
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); }
+#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
#endif // QUUID_H