diff options
Diffstat (limited to 'src/corelib/plugin')
24 files changed, 1092 insertions, 1376 deletions
diff --git a/src/corelib/plugin/qcoffpeparser.cpp b/src/corelib/plugin/qcoffpeparser.cpp index f258ef5de2..639e402a07 100644 --- a/src/corelib/plugin/qcoffpeparser.cpp +++ b/src/corelib/plugin/qcoffpeparser.cpp @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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) 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" @@ -45,21 +9,19 @@ #include <optional> -#define WIN32_LEAN_AND_MEAN -#include <windows.h> -#undef min -#undef max +#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 char rawSectionName[] = ".qtmetadata"; -static constexpr QLatin1String metadataSectionName(rawSectionName, sizeof(rawSectionName) - 1); -static constexpr QLatin1String truncatedSectionName = - metadataSectionName.left(sizeof(IMAGE_SECTION_HEADER::Name)); +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 @@ -78,7 +40,7 @@ static const WORD ExpectedMachine = #if 0 // nothing, just so everything is #elf #elif defined(Q_PROCESSOR_ARM_32) - IMAGE_FILE_MACHINE_ARM + IMAGE_FILE_MACHINE_ARMNT #elif defined(Q_PROCESSOR_ARM_64) IMAGE_FILE_MACHINE_ARM64 #elif defined(Q_PROCESSOR_IA64) @@ -155,7 +117,7 @@ Q_DECL_UNUSED static QDebug &operator<<(QDebug &d, HeaderDebug h) switch (h.h->FileHeader.Machine) { case IMAGE_FILE_MACHINE_I386: d << "i386"; break; case IMAGE_FILE_MACHINE_ARM: d << "ARM"; break; - case IMAGE_FILE_MACHINE_ARMNT: d << "ARM Thumb-2"; break;; + case IMAGE_FILE_MACHINE_ARMNT: d << "ARM Thumb-2"; break; case IMAGE_FILE_MACHINE_THUMB: d << "Thumb"; break; case IMAGE_FILE_MACHINE_IA64: d << "IA-64"; break; case IMAGE_FILE_MACHINE_MIPS16: d << "MIPS16"; break; @@ -330,7 +292,7 @@ findStringTable(QByteArrayView data, const IMAGE_NT_HEADERS *ntHeader, const Err return data.sliced(off, size); } -static QLatin1String findSectionName(const IMAGE_SECTION_HEADER *section, QByteArrayView stringTable) +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)); @@ -346,13 +308,13 @@ static QLatin1String findSectionName(const IMAGE_SECTION_HEADER *section, QByteA bool ok; qsizetype offset = QByteArrayView(ptr + 1, n - 1).toUInt(&ok); if (!ok || offset >= stringTable.size()) - return QLatin1String(); + return {}; ptr = stringTable.data() + offset; n = qstrnlen(ptr, stringTable.size() - offset); } - return QLatin1String(ptr, n); + return {ptr, n}; } QLibraryScanResult QCoffPeParser::parse(QByteArrayView data, QString *errMsg) @@ -367,7 +329,7 @@ QLibraryScanResult QCoffPeParser::parse(QByteArrayView data, QString *errMsg) return {}; QByteArrayView stringTable; - if (auto optional = findStringTable(data, ntHeaders, error); optional) + if (auto optional = findStringTable(data, ntHeaders, error)) stringTable = *optional; else return {}; @@ -375,7 +337,7 @@ QLibraryScanResult QCoffPeParser::parse(QByteArrayView data, QString *errMsg) // scan the sections now const auto sectionTableEnd = section + ntHeaders->FileHeader.NumberOfSections; for ( ; section < sectionTableEnd; ++section) { - QLatin1String sectionName = findSectionName(section, stringTable); + 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")); @@ -383,7 +345,7 @@ QLibraryScanResult QCoffPeParser::parse(QByteArrayView data, QString *errMsg) 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("a section data extends past the end of the file")); + 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 @@ -392,7 +354,7 @@ QLibraryScanResult QCoffPeParser::parse(QByteArrayView data, QString *errMsg) continue; // if we do have a string table, the name may be complete - if (sectionName != truncatedSectionName && sectionName != metadataSectionName) + if (sectionName != truncatedSectionName && sectionName != metadataSectionName()) continue; peDebug << "found .qtmetadata section"; diff --git a/src/corelib/plugin/qcoffpeparser_p.h b/src/corelib/plugin/qcoffpeparser_p.h index 91de6a416b..19c650fee0 100644 --- a/src/corelib/plugin/qcoffpeparser_p.h +++ b/src/corelib/plugin/qcoffpeparser_p.h @@ -1,41 +1,5 @@ -/**************************************************************************** -** -** 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) 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 diff --git a/src/corelib/plugin/qelfparser_p.cpp b/src/corelib/plugin/qelfparser_p.cpp index 6a83c94e20..7f6271cde4 100644 --- a/src/corelib/plugin/qelfparser_p.cpp +++ b/src/corelib/plugin/qelfparser_p.cpp @@ -1,46 +1,10 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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) 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) && __has_include(<elf.h>) +#ifdef Q_OF_ELF #include "qlibrary_p.h" @@ -48,10 +12,18 @@ #include <qnumeric.h> #include <qsysinfo.h> -#include <elf.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 +using namespace Qt::StringLiterals; + // ### Qt7: propagate the constant and eliminate dead code static constexpr bool ElfNotesAreMandatory = QT_VERSION >= QT_VERSION_CHECK(7,0,0); @@ -82,6 +54,10 @@ static Q_LOGGING_CATEGORY(lcElfParser, "qt.core.plugin.elfparser") # define PT_GNU_PROPERTY 0x6474e553 #endif +#ifndef PN_XNUM +# define PN_XNUM 0xffff +#endif + QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wunused-const-variable") @@ -135,14 +111,22 @@ struct ElfMachineCheck static const Elf32_Half ExpectedMachine = #if 0 // nothing +#elif defined(Q_PROCESSOR_ALPHA) + EM_ALPHA #elif defined(Q_PROCESSOR_ARM_32) EM_ARM #elif defined(Q_PROCESSOR_ARM_64) EM_AARCH64 #elif defined(Q_PROCESSOR_BLACKFIN) EM_BLACKFIN +#elif defined(Q_PROCESSOR_HPPA) + EM_PARISC #elif defined(Q_PROCESSOR_IA64) EM_IA_64 +#elif defined(Q_PROCESSOR_LOONGARCH) + EM_LOONGARCH +#elif defined(Q_PROCESSOR_M68K) + EM_68K #elif defined(Q_PROCESSOR_MIPS) EM_MIPS #elif defined(Q_PROCESSOR_POWER_32) @@ -156,10 +140,8 @@ struct ElfMachineCheck #elif defined(Q_PROCESSOR_SH) EM_SH #elif defined(Q_PROCESSOR_SPARC_V9) -# warning "Please confirm that this is correct for Linux and Solaris" EM_SPARCV9 #elif defined(Q_PROCESSOR_SPARC_64) -# warning "Please confirm that this is correct for Linux and Solaris" EM_SPARCV9 #elif defined(Q_PROCESSOR_SPARC) EM_SPARC @@ -357,7 +339,7 @@ Q_DECL_UNUSED Q_DECL_COLD_FUNCTION static QDebug &operator<<(QDebug &d, ElfHeade case ELFOSABI_SYSV: d << " (SYSV"; break; case ELFOSABI_HPUX: d << " (HP-UX"; break; case ELFOSABI_NETBSD: d << " (NetBSD"; break; - case ELFOSABI_GNU: d << " (GNU/Linux"; break; + case ELFOSABI_LINUX: d << " (GNU/Linux"; break; case ELFOSABI_SOLARIS: d << " (Solaris"; break; case ELFOSABI_AIX: d << " (AIX"; break; case ELFOSABI_IRIX: d << " (IRIX"; break; @@ -401,15 +383,27 @@ Q_DECL_UNUSED Q_DECL_COLD_FUNCTION static QDebug &operator<<(QDebug &d, ElfHeade switch (r.machine) { // list definitely not exhaustive! case EM_NONE: d << ", no machine"; break; + case EM_ALPHA: d << ", Alpha"; break; + case EM_68K: d << ", MC68000"; break; case EM_ARM: d << ", ARM"; break; case EM_AARCH64: d << ", AArch64"; break; +#ifdef EM_BLACKFIN case EM_BLACKFIN: d << ", Blackfin"; break; +#endif case EM_IA_64: d << ", IA-64"; break; +#ifdef EM_LOONGARCH + case EM_LOONGARCH: d << ", LoongArch"; break; +#endif case EM_MIPS: d << ", MIPS"; break; + case EM_PARISC: d << ", HPPA"; break; case EM_PPC: d << ", PowerPC"; break; case EM_PPC64: d << ", PowerPC 64-bit"; break; +#ifdef EM_RISCV case EM_RISCV: d << ", RISC-V"; break; +#endif +#ifdef EM_S390 case EM_S390: d << ", S/390"; break; +#endif case EM_SH: d << ", SuperH"; break; case EM_SPARC: d << ", SPARC"; break; case EM_SPARCV9: d << ", SPARCv9"; break; @@ -549,6 +543,8 @@ static bool preScanProgramHeaders(QByteArrayView data, const ErrorMaker &error) // first, validate the extent of the full program header table T::Word e_phnum = header->e_phnum; + if (e_phnum == PN_XNUM) + return error(QLibrary::tr("unimplemented: PN_XNUM program headers")), false; T::Off offset = e_phnum * sizeof(T::Phdr); // can't overflow due to size of T::Half if (qAddOverflow(offset, header->e_phoff, &offset) || offset > size_t(data.size())) return error(QLibrary::tr("program header table extends past the end of the file")), false; @@ -688,11 +684,11 @@ static QLibraryScanResult scanSections(QByteArrayView data, const ErrorMaker &er const char *shstrtab_start = data.data() + offset; shdr = sections; for (int section = 0; shdr != sections_end; ++section, ++shdr) { - QLatin1String name; + 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 = QLatin1String(namestart, len); + name = QLatin1StringView(namestart, len); } qEDebug << "section" << section << "name" << name << ElfSectionDebug{shdr}; @@ -703,13 +699,13 @@ static QLibraryScanResult scanSections(QByteArrayView data, const ErrorMaker &er // sections aren't allowed to extend past the end of the file, unless // they are NOBITS sections if (shdr->sh_type == SHT_NOBITS) - continue;; + continue; if (T::Off end; qAddOverflow(shdr->sh_offset, shdr->sh_size, &end) || end > size_t(data.size())) { - return error(QLibrary::tr("a section data extends past the end of the file")); + return error(QLibrary::tr("section contents extend past the end of the file")); } - if (name != QLatin1String(".qtmetadata")) + if (name != ".qtmetadata"_L1) continue; qEDebug << "found .qtmetadata section"; if (shdr->sh_size < sizeof(QPluginMetaData::MagicHeader)) @@ -790,4 +786,4 @@ QLibraryScanResult QElfParser::parse(QByteArrayView data, QString *errMsg) 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 42eb212ee1..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 @@ -56,7 +20,7 @@ QT_REQUIRE_CONFIG(library); -#if defined(Q_OF_ELF) && __has_include(<elf.h>) +#ifdef Q_OF_ELF QT_BEGIN_NAMESPACE @@ -67,6 +31,6 @@ struct QElfParser 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 79423251a4..e2d9a40cb4 100644 --- a/src/corelib/plugin/qfactoryloader.cpp +++ b/src/corelib/plugin/qfactoryloader.cpp @@ -1,62 +1,23 @@ -/**************************************************************************** -** -** 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 "private/qcoreapplication_p.h" #include "private/qduplicatetracker_p.h" #include "private/qloggingregistry_p.h" #include "private/qobject_p.h" #include "qcborarray.h" #include "qcbormap.h" +#include "qcborstreamreader.h" #include "qcborvalue.h" -#include "qcborvalue.h" -#include "qdir.h" +#include "qdirlisting.h" #include "qfileinfo.h" #include "qjsonarray.h" #include "qjsondocument.h" #include "qjsonobject.h" -#include "qmap.h" #include "qmutex.h" #include "qplugin.h" #include "qplugin_p.h" @@ -68,28 +29,189 @@ #include <qtcore_tracepoints_p.h> +#include <map> +#include <vector> + QT_BEGIN_NAMESPACE -bool QPluginParsedMetaData::parse(QByteArrayView raw) +using namespace Qt::StringLiterals; + +Q_TRACE_POINT(qtcore, QFactoryLoader_update, const QString &fileName); + +namespace { +struct IterationResult +{ + enum Result { + FinishedSearch = 0, + ContinueSearch, + + // parse errors + ParsingError = -1, + InvalidMetaDataVersion = -2, + InvalidTopLevelItem = -3, + InvalidHeaderItem = -4, + }; + Result result; + QCborError error = { QCborError::NoError }; + + Q_IMPLICIT IterationResult(Result r) : result(r) {} + Q_IMPLICIT IterationResult(QCborError e) : result(ParsingError), error(e) {} +}; + +struct QFactoryLoaderIidSearch +{ + QLatin1StringView iid; + bool matchesIid = false; + QFactoryLoaderIidSearch(QLatin1StringView iid) : iid(iid) + { Q_ASSERT(!iid.isEmpty()); } + + static IterationResult::Result skip(QCborStreamReader &reader) + { + // skip this, whatever it is + reader.next(); + return IterationResult::ContinueSearch; + } + + IterationResult::Result operator()(QtPluginMetaDataKeys key, QCborStreamReader &reader) + { + if (key != QtPluginMetaDataKeys::IID) + return skip(reader); + matchesIid = (reader.readAllString() == iid); + return IterationResult::FinishedSearch; + } + IterationResult::Result operator()(QUtf8StringView, QCborStreamReader &reader) + { + return skip(reader); + } +}; + +struct QFactoryLoaderMetaDataKeysExtractor : QFactoryLoaderIidSearch +{ + QCborArray keys; + QFactoryLoaderMetaDataKeysExtractor(QLatin1StringView iid) + : QFactoryLoaderIidSearch(iid) + {} + + IterationResult::Result operator()(QtPluginMetaDataKeys key, QCborStreamReader &reader) + { + if (key == QtPluginMetaDataKeys::IID) { + QFactoryLoaderIidSearch::operator()(key, reader); + return IterationResult::ContinueSearch; + } + if (key != QtPluginMetaDataKeys::MetaData) + return skip(reader); + + if (!matchesIid) + return IterationResult::FinishedSearch; + if (!reader.isMap() || !reader.isLengthKnown()) + return IterationResult::InvalidHeaderItem; + if (!reader.enterContainer()) + return IterationResult::ParsingError; + while (reader.isValid()) { + // the metadata is JSON, so keys are all strings + QByteArray key = reader.readAllUtf8String(); + if (key == "Keys") { + if (!reader.isArray() || !reader.isLengthKnown()) + return IterationResult::InvalidHeaderItem; + keys = QCborValue::fromCbor(reader).toArray(); + break; + } + skip(reader); + } + // warning: we may not have finished iterating over the header + return IterationResult::FinishedSearch; + } + using QFactoryLoaderIidSearch::operator(); +}; +} // unnamed namespace + +template <typename F> static IterationResult iterateInPluginMetaData(QByteArrayView raw, F &&f) { QPluginMetaData::Header header; Q_ASSERT(raw.size() >= qsizetype(sizeof(header))); memcpy(&header, raw.data(), sizeof(header)); if (Q_UNLIKELY(header.version > QPluginMetaData::CurrentMetaDataVersion)) - return setError(QFactoryLoader::tr("Invalid metadata version")); + return IterationResult::InvalidMetaDataVersion; // use fromRawData to keep QCborStreamReader from copying raw = raw.sliced(sizeof(header)); QByteArray ba = QByteArray::fromRawData(raw.data(), raw.size()); - QCborParserError err; - QCborValue metadata = QCborValue::fromCbor(ba, &err); + QCborStreamReader reader(ba); + if (reader.isInvalid()) + return reader.lastError(); + if (!reader.isMap()) + return IterationResult::InvalidTopLevelItem; + if (!reader.enterContainer()) + return reader.lastError(); + while (reader.isValid()) { + IterationResult::Result r; + if (reader.isInteger()) { + // integer key, one of ours + qint64 value = reader.toInteger(); + auto key = QtPluginMetaDataKeys(value); + if (qint64(key) != value) + return IterationResult::InvalidHeaderItem; + if (!reader.next()) + return reader.lastError(); + r = f(key, reader); + } else if (reader.isString()) { + QByteArray key = reader.readAllUtf8String(); + if (key.isNull()) + return reader.lastError(); + r = f(QUtf8StringView(key), reader); + } else { + return IterationResult::InvalidTopLevelItem; + } + + if (QCborError e = reader.lastError()) + return e; + if (r != IterationResult::ContinueSearch) + return r; + } - if (err.error != QCborError::NoError) - return setError(QFactoryLoader::tr("Metadata parsing error: %1").arg(err.error.toString())); - if (!metadata.isMap()) + if (!reader.leaveContainer()) + return reader.lastError(); + return IterationResult::FinishedSearch; +} + +static bool isIidMatch(QByteArrayView raw, QLatin1StringView iid) +{ + QFactoryLoaderIidSearch search(iid); + iterateInPluginMetaData(raw, search); + return search.matchesIid; +} + +bool QPluginParsedMetaData::parse(QByteArrayView raw) +{ + QCborMap map; + auto r = iterateInPluginMetaData(raw, [&](const auto &key, QCborStreamReader &reader) { + QCborValue item = QCborValue::fromCbor(reader); + if (item.isInvalid()) + return IterationResult::ParsingError; + if constexpr (std::is_enum_v<std::decay_t<decltype(key)>>) + map[int(key)] = item; + else + map[QString::fromUtf8(key)] = item; + return IterationResult::ContinueSearch; + }); + + switch (r.result) { + case IterationResult::FinishedSearch: + case IterationResult::ContinueSearch: + break; + + // parse errors + case IterationResult::ParsingError: + return setError(QFactoryLoader::tr("Metadata parsing error: %1").arg(r.error.toString())); + case IterationResult::InvalidMetaDataVersion: + return setError(QFactoryLoader::tr("Invalid metadata version")); + case IterationResult::InvalidTopLevelItem: + case IterationResult::InvalidHeaderItem: return setError(QFactoryLoader::tr("Unexpected metadata contents")); - QCborMap map = metadata.toMap(); - metadata = {}; + } + + // header was validated + auto header = qFromUnaligned<QPluginMetaData::Header>(raw.data()); DecodedArchRequirements archReq = header.version == 0 ? decodeVersion0ArchRequirements(header.plugin_arch_requirements) @@ -130,17 +252,21 @@ QJsonObject QPluginParsedMetaData::toJson() const 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 }; @@ -149,128 +275,134 @@ public: static Q_LOGGING_CATEGORY_WITH_ENV_OVERRIDE(lcFactoryLoader, "QT_DEBUG_PLUGINS", "qt.core.plugin.factoryloader") -Q_GLOBAL_STATIC(QList<QFactoryLoader *>, qt_factory_loaders) +namespace { +struct QFactoryLoaderGlobals +{ + // needs to be recursive because loading one plugin could cause another + // factory to be initialized + QRecursiveMutex mutex; + QList<QFactoryLoader *> loaders; +}; +} -Q_GLOBAL_STATIC(QRecursiveMutex, qt_factoryloader_mutex) +Q_GLOBAL_STATIC(QFactoryLoaderGlobals, qt_factoryloader_global) QFactoryLoaderPrivate::~QFactoryLoaderPrivate() -{ - for (QLibraryPrivate *library : qAsConst(libraryList)) - library->release(); -} + = default; -void QFactoryLoader::update() +inline void QFactoryLoaderPrivate::updateSinglePath(const QString &path) { -#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; - -#ifdef Q_OS_ANDROID - QString path = pluginDir; -#else - QString path = pluginDir + d->suffix; -#endif + struct LibraryReleaser { + void operator()(QLibraryPrivate *library) + { if (library) library->release(); } + }; - qCDebug(lcFactoryLoader) << "checking directory path" << path << "..."; + // If we've already loaded, skip it... + if (loadedPaths.hasSeen(path)) + return; - if (!QDir(path).exists(QLatin1String("."))) - continue; + qCDebug(lcFactoryLoader) << "checking directory path" << path << "..."; - QStringList plugins = QDir(path).entryList( + 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 - qCDebug(lcFactoryLoader) << "looking at" << fileName; - - Q_TRACE(QFactoryLoader_update, fileName); + qCDebug(lcFactoryLoader) << "looking at" << fileName; - library = QLibraryPrivate::findOrCreate(QFileInfo(fileName).canonicalFilePath()); - if (!library->isPlugin()) { - qCDebug(lcFactoryLoader) << 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(QtPluginMetaDataKeys::IID).toString(); - if (iid == QLatin1String(d->iid.constData(), d->iid.size())) { - QCborMap object = library->metaData.value(QtPluginMetaDataKeys::MetaData).toMap(); - metaDataOk = true; + QStringList keys; + bool metaDataOk = false; - QCborArray 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(); - } - qCDebug(lcFactoryLoader) << "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 - constexpr int QtVersionNoPatch = QT_VERSION_CHECK(QT_VERSION_MAJOR, QT_VERSION_MINOR, 0); - 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(QtPluginMetaDataKeys::QtVersion).toInteger()); - int qt_version = int(library->metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger()); - if (!previous || (prev_qt_version > QtVersionNoPatch && qt_version <= QtVersionNoPatch)) { - 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); qCDebug(lcFactoryLoader) << "ignoring" << d->iid @@ -280,25 +412,29 @@ void QFactoryLoader::update() 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(); } } @@ -309,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; @@ -316,30 +455,54 @@ 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 } +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<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 - QLatin1String iid(d->iid.constData(), d->iid.size()); + 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); @@ -348,6 +511,37 @@ QFactoryLoader::MetaDataList QFactoryLoader::metaData() const continue; metaData.append(std::move(parsed)); } + + // other portions of the code will cast to int (e.g., keyMap()) + Q_ASSERT(metaData.size() <= std::numeric_limits<int>::max()); + return metaData; +} + +QList<QCborArray> QFactoryLoader::metaDataKeys() const +{ + Q_D(const QFactoryLoader); + QList<QCborArray> metaData; +#if QT_CONFIG(library) + QMutexLocker locker(&d->mutex); + for (const auto &library : d->libraries) { + const QCborValue md = library->metaData.value(QtPluginMetaDataKeys::MetaData); + metaData.append(md["Keys"_L1].toArray()); + } +#endif + + QLatin1StringView iid(d->iid.constData(), d->iid.size()); + const auto staticPlugins = QPluginLoader::staticPlugins(); + for (const QStaticPlugin &plugin : staticPlugins) { + QByteArrayView pluginData(static_cast<const char *>(plugin.rawMetaData), + plugin.rawMetaDataSize); + QFactoryLoaderMetaDataKeysExtractor extractor{ iid }; + iterateInPluginMetaData(pluginData, extractor); + if (extractor.matchesIid) + metaData += std::move(extractor.keys); + } + + // other portions of the code will cast to int (e.g., keyMap()) + Q_ASSERT(metaData.size() <= std::numeric_limits<int>::max()); return metaData; } @@ -359,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()); @@ -368,16 +562,16 @@ 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 - QLatin1String iid(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); - QPluginParsedMetaData parsed(pluginData); - if (parsed.isError() || parsed.value(QtPluginMetaDataKeys::IID) != iid) + if (!isIidMatch(pluginData, iid)) continue; if (index == 0) @@ -391,26 +585,22 @@ QObject *QFactoryLoader::instance(int index) const QMultiMap<int, QString> QFactoryLoader::keyMap() const { QMultiMap<int, QString> result; - const QList<QPluginParsedMetaData> metaDataList = metaData(); - for (int i = 0; i < metaDataList.size(); ++i) { - const QCborMap metaData = metaDataList.at(i).value(QtPluginMetaDataKeys::MetaData).toMap(); - const QCborArray keys = metaData.value(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<QPluginParsedMetaData> metaDataList = metaData(); - for (int i = 0; i < metaDataList.size(); ++i) { - const QCborMap metaData = metaDataList.at(i).value(QtPluginMetaDataKeys::MetaData).toMap(); - const QCborArray keys = metaData.value(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 f7033d144f..56dc7e6ad1 100644 --- a/src/corelib/plugin/qfactoryloader_p.h +++ b/src/corelib/plugin/qfactoryloader_p.h @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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) 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 @@ -90,7 +54,7 @@ public: // 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 toCbor()[int(k)]; } + QCborValue value(QtPluginMetaDataKeys k) const { return data[int(k)]; } }; class QFactoryLoaderPrivate; @@ -110,17 +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; 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 d4ee833554..a3ef8e3c52 100644 --- a/src/corelib/plugin/qlibrary.cpp +++ b/src/corelib/plugin/qlibrary.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** 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 #include "qlibrary.h" #include "qlibrary_p.h" @@ -47,12 +11,11 @@ #include <qfile.h> #include <qfileinfo.h> #include <qjsondocument.h> -#include <qmap.h> #include <qmutex.h> #include <qoperatingsystemversion.h> #include <qstringlist.h> -#ifdef Q_OS_MAC +#ifdef Q_OS_DARWIN # include <private/qcore_mac_p.h> #endif #include <private/qcoreapplication_p.h> @@ -66,8 +29,15 @@ #include <qtcore_tracepoints_p.h> +#include <QtCore/q20map.h> + QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + +Q_TRACE_POINT(qtcore, QLibraryPrivate_load_entry, const QString &fileName); +Q_TRACE_POINT(qtcore, QLibraryPrivate_load_exit, bool success); + // On Unix systema and on Windows with MinGW, we can mix and match debug and // release plugins without problems. (unless compiled in debug-and-release mode // - why?) @@ -203,16 +173,26 @@ static QLibraryScanResult qt_find_pattern(const char *s, qsizetype s_len, QStrin 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) && __has_include(<elf.h>) +#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 - QByteArrayView pattern = QPluginMetaData::MagicString; - static const QByteArrayMatcher matcher(pattern.toByteArray()); - qsizetype i = matcher.indexIn(s, s_len); + 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{}; @@ -231,7 +211,7 @@ static QLibraryScanResult qt_find_pattern(const char *s, qsizetype s_len, QStrin information could not be read. Returns true if version information is present and successfully read. */ -static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) +static QLibraryScanResult findPatternUnloaded(const QString &library, QLibraryPrivate *lib) { QFile file(library); if (!file.open(QIODevice::ReadOnly)) { @@ -239,7 +219,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) lib->errorString = file.errorString(); qCWarning(qt_lcDebugPlugins, "%ls: cannot open: %ls", qUtf16Printable(library), qUtf16Printable(file.errorString())); - return false; + return {}; } // Files can be bigger than the virtual memory size on 32-bit systems, so @@ -256,7 +236,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) // This can't be used as a plugin. qCWarning(qt_lcDebugPlugins, "%ls: failed to map to memory: %ls", qUtf16Printable(library), qUtf16Printable(file.errorString())); - return false; + return {}; } #else QByteArray data; @@ -273,15 +253,19 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) QString errMsg = library; QLibraryScanResult r = qt_find_pattern(filedata, fdlen, &errMsg); if (r.length) { +#if defined(Q_OF_MACH_O) + if (r.isEncrypted) + return r; +#endif if (!lib->metaData.parse(QByteArrayView(filedata + r.pos, r.length))) { errMsg = lib->metaData.errorString(); - qCWarning(qt_lcDebugPlugins, "Found invalid metadata in lib %ls: %ls", + qCDebug(qt_lcDebugPlugins, "Found invalid metadata in lib %ls: %ls", qUtf16Printable(library), qUtf16Printable(errMsg)); } else { qCDebug(qt_lcDebugPlugins, "Found metadata in lib %ls, metadata=\n%s\n", qUtf16Printable(library), QJsonDocument(lib->metaData.toJson()).toJson().constData()); - return true; + return r; } } else { qCDebug(qt_lcDebugPlugins, "Failed to find metadata in lib %ls: %ls", @@ -290,7 +274,7 @@ static bool findPatternUnloaded(const QString &library, QLibraryPrivate *lib) lib->errorString = QLibrary::tr("Failed to extract plugin meta data from '%1': %2") .arg(library, errMsg); - return false; + return {}; } static void installCoverageTool(QLibraryPrivate *libPrivate) @@ -338,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() { @@ -358,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. @@ -379,14 +356,13 @@ inline void QLibraryStore::cleanup() lib->unload(); #endif } - delete lib; - it.value() = nullptr; + delete std::exchange(lib, nullptr); } } // dump all objects that remain if (lcDebugLibrary().isDebugEnabled()) { - for (QLibraryPrivate *lib : qAsConst(data->libraryMap)) { + for (auto &[_, lib] : data->libraryMap) { if (lib) qDebug(lcDebugLibrary) << "On QtCore unload," << lib->fileName << "was leaked, with" @@ -417,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; } @@ -452,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; } @@ -483,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) @@ -495,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); @@ -618,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,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 @@ -745,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. @@ -768,17 +780,17 @@ void QLibraryPrivate::updatePluginState() uint qt_version = uint(metaData.value(QtPluginMetaDataKeys::QtVersion).toInteger()); bool debug = metaData.value(QtPluginMetaDataKeys::IsDebug).toBool(); if ((qt_version & 0x00ff00) > (QT_VERSION & 0x00ff00) || (qt_version & 0xff0000) != (QT_VERSION & 0xff0000)) { - qCWarning(qt_lcDebugPlugins, "In %s:\n" + qCDebug(qt_lcDebugPlugins, "In %s:\n" " Plugin uses incompatible Qt library (%d.%d.%d) [%s]", QFile::encodeName(fileName).constData(), (qt_version&0xff0000) >> 16, (qt_version&0xff00) >> 8, qt_version&0xff, 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")); + .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." @@ -804,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; } /*! @@ -820,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() */ @@ -834,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; } @@ -934,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 @@ -963,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()); } /*! @@ -987,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 } /*! @@ -1124,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 42d3d7256d..87d36ee5c8 100644 --- a/src/corelib/plugin/qlibrary_p.h +++ b/src/corelib/plugin/qlibrary_p.h @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** 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 QLIBRARY_P_H #define QLIBRARY_P_H @@ -64,6 +28,8 @@ # include "QtCore/qt_windows.h" #endif +#include <memory> + QT_REQUIRE_CONFIG(library); QT_BEGIN_NAMESPACE @@ -74,6 +40,9 @@ struct QLibraryScanResult { qsizetype pos; qsizetype length; +#if defined(Q_OF_MACH_O) + bool isEncrypted = false; +#endif }; class QLibraryStore; @@ -87,6 +56,12 @@ public: #endif enum UnloadFlag { UnloadSys, NoUnloadSys }; + struct Deleter { + // QLibraryPrivate::release() is not, yet, and cannot easily be made, noexcept: + void operator()(QLibraryPrivate *p) const { p->release(); } + }; + using UniquePtr = std::unique_ptr<QLibraryPrivate, Deleter>; + const QString fileName; const QString fullVersion; diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp index c119bee2fc..a6fb5403cd 100644 --- a/src/corelib/plugin/qlibrary_unix.cpp +++ b/src/corelib/plugin/qlibrary_unix.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** 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" @@ -48,7 +12,7 @@ #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 eab7255a7a..c95118e554 100644 --- a/src/corelib/plugin/qlibrary_win.cpp +++ b/src/corelib/plugin/qlibrary_win.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 "qplatformdefs.h" #include "qlibrary_p.h" @@ -49,6 +13,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + extern QString qt_error_string(int code); QStringList QLibraryPrivate::suffixes_sys(const QString& fullVersion) @@ -81,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 @@ -93,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 @@ -115,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 310d8e06c3..7a82b84cb3 100644 --- a/src/corelib/plugin/qmachparser.cpp +++ b/src/corelib/plugin/qmachparser.cpp @@ -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 #include "qmachparser_p.h" @@ -46,6 +10,8 @@ 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; @@ -90,6 +56,23 @@ static QLibraryScanResult notfound(const QString &reason, QString *errorString) return {}; } +static bool isEncrypted(const my_mach_header *header) +{ + auto commandCursor = uintptr_t(header) + sizeof(my_mach_header); + for (uint32_t i = 0; i < header->ncmds; ++i) { + load_command *loadCommand = reinterpret_cast<load_command *>(commandCursor); + if (loadCommand->cmd == LC_ENCRYPTION_INFO || loadCommand->cmd == LC_ENCRYPTION_INFO_64) { + // The layout of encryption_info_command and encryption_info_command_64 is the same + // up until and including cryptid, so we can treat it as encryption_info_command. + auto encryptionInfoCommand = reinterpret_cast<encryption_info_command*>(loadCommand); + return encryptionInfoCommand->cryptid != 0; + } + commandCursor += loadCommand->cmdsize; + } + + return false; +} + QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *errorString) { // The minimum size of a Mach-O binary we're interested in. @@ -143,7 +126,8 @@ QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *e // check magic if (header->magic != my_magic) - return notfound(QLibrary::tr("invalid magic %1").arg(qFromBigEndian(header->magic), 8, 16, QLatin1Char('0')), + return notfound(QLibrary::tr("invalid magic %1").arg(qFromBigEndian(header->magic), + 8, 16, '0'_L1), errorString); } @@ -199,8 +183,12 @@ QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *e if (sect[j].size < sizeof(QPluginMetaData::MagicHeader)) return notfound(QLibrary::tr(".qtmetadata section is too small"), errorString); + const bool binaryIsEncrypted = isEncrypted(header); qsizetype pos = reinterpret_cast<const char *>(header) - m_s + sect[j].offset; - if (IncludeValidityChecks) { + + // We can not read the section data of encrypted libraries until they + // have been dlopened(), so skip validity check if that's the case. + if (IncludeValidityChecks && !binaryIsEncrypted) { QByteArrayView expectedMagic = QByteArrayView::fromArray(QPluginMetaData::MagicString); QByteArrayView actualMagic = QByteArrayView(m_s + pos, expectedMagic.size()); if (expectedMagic != actualMagic) @@ -208,7 +196,7 @@ QLibraryScanResult QMachOParser::parse(const char *m_s, ulong fdlen, QString *e } pos += sizeof(QPluginMetaData::MagicString); - return { pos, qsizetype(sect[j].size - sizeof(QPluginMetaData::MagicString)) }; + return { pos, qsizetype(sect[j].size - sizeof(QPluginMetaData::MagicString)), binaryIsEncrypted }; } } diff --git a/src/corelib/plugin/qmachparser_p.h b/src/corelib/plugin/qmachparser_p.h index 9e0375c6cb..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 @@ -70,6 +34,6 @@ public: 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 6efdcfe7f6..909c8acdcc 100644 --- a/src/corelib/plugin/qplugin.h +++ b/src/corelib/plugin/qplugin.h @@ -1,50 +1,20 @@ -/**************************************************************************** -** -** 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 @@ -74,10 +44,8 @@ struct QPluginMetaData template <size_t OSize, typename OO, size_t ISize, typename II> static constexpr void copy(OO (&out)[OSize], II (&in)[ISize]) { - // std::copy is not constexpr until C++20 static_assert(OSize <= ISize, "Output would not be fully initialized"); - for (size_t i = 0; i < OSize; ++i) - out[i] = in[i]; + q20::copy_n(in, OSize, out); } static constexpr quint8 archRequirements() @@ -159,7 +127,7 @@ void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin staticPlugin); #if defined(Q_OF_ELF) || (defined(Q_OS_WIN) && (defined (Q_CC_GNU) || defined(Q_CC_CLANG))) # define QT_PLUGIN_METADATA_SECTION \ __attribute__ ((section (".qtmetadata"))) __attribute__((used)) -#elif defined(Q_OS_MAC) +#elif defined(Q_OS_DARWIN) # define QT_PLUGIN_METADATA_SECTION \ __attribute__ ((section ("__TEXT,qtmetadata"))) __attribute__((used)) #elif defined(Q_CC_MSVC) diff --git a/src/corelib/plugin/qplugin.qdoc b/src/corelib/plugin/qplugin.qdoc index 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 026992ba97..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 diff --git a/src/corelib/plugin/qpluginloader.cpp b/src/corelib/plugin/qpluginloader.cpp index fb69142149..03b8cfbb84 100644 --- a/src/corelib/plugin/qpluginloader.cpp +++ b/src/corelib/plugin/qpluginloader.cpp @@ -1,42 +1,6 @@ -/**************************************************************************** -** -** 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) 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" @@ -53,6 +17,8 @@ QT_BEGIN_NAMESPACE +using namespace Qt::StringLiterals; + #if QT_CONFIG(library) /*! @@ -105,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. */ @@ -131,7 +99,7 @@ QPluginLoader::QPluginLoader(const QString &fileName, QObject *parent) : QObject(parent), d(nullptr), did_load(false) { setFileName(fileName); - setLoadHints(QLibrary::PreventUnloadHint); + setLoadHints(defaultLoadHints); } /*! @@ -267,7 +235,7 @@ 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 '/' @@ -278,19 +246,19 @@ 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('_')); + 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; + const QString fn = path + u'/' + basePath + prefix + baseName + suffix; qCDebug(qt_lcDebugPlugins) << "Trying..." << fn; if (QFileInfo(fn).isFile()) return fn; @@ -327,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(); @@ -381,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) @@ -406,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); } /*! @@ -417,12 +403,11 @@ void Q_CORE_EXPORT qRegisterStaticPluginFunction(QStaticPlugin plugin) QObjectList QPluginLoader::staticInstances() { QObjectList instances; - const StaticPluginList *plugins = staticPluginList(); - if (plugins) { - const int numPlugins = plugins->size(); - instances.reserve(numPlugins); - for (int i = 0; i < numPlugins; ++i) - instances += plugins->at(i).instance(); + if (staticPluginList.exists()) { + const StaticPluginList &plugins = *staticPluginList; + instances.reserve(plugins.size()); + for (QStaticPlugin plugin : plugins) + instances += plugin.instance(); } return instances; } diff --git a/src/corelib/plugin/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 |