From a1db2959129cb4630adfffcaaece19bafe16db77 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Mon, 17 Jun 2024 14:22:13 -0700 Subject: [PATCH] QLibrary/Unix: update the x86-64-v3 prefix path in our search glibc introduced the glibc-hwcaps/XXXX path in glibc 2.33 (2020) and removed the old, legacy "haswell/" prefix in glibc 2.37 (2022). This means anyone deploying HW-capable libraries must be deploying symlinks, so we are not losing functionality. Because it says "glibc-hwcaps", I am now making this dependent on glibc for libraries. Added unit testing for this feature. Tested on Linux, FreeBSD, macOS, and Windows (the QLibrary test SKIPs everywhere except Linux). We do create a "libtheplugin.dylib.avx2" on macOS with this change, but won't attempt to load it (Darwin has fat binaries so lipo(1)ing the files together would be the right thing to do). Pick-to: 6.8 Change-Id: Ic0adfa808d28487a8303fffd17d9e78ec87bbd9a Reviewed-by: Fabian Kosmale --- src/corelib/plugin/qlibrary_unix.cpp | 6 ++- .../corelib/plugin/qlibrary/CMakeLists.txt | 1 + .../plugin/qlibrary/archlib/CMakeLists.txt | 38 ++++++++++++++++++ .../corelib/plugin/qlibrary/archlib/mylib.c | 13 ++++++ .../corelib/plugin/qlibrary/tst_qlibrary.cpp | 40 +++++++++++++++++++ .../qpluginloader/theplugin/CMakeLists.txt | 18 +++++++++ .../qpluginloader/theplugin/plugininterface.h | 1 + .../qpluginloader/theplugin/theplugin.cpp | 10 +++++ .../qpluginloader/theplugin/theplugin.h | 1 + .../qpluginloader/tst_qpluginloader.cpp | 21 ++++++++++ 10 files changed, 147 insertions(+), 2 deletions(-) create mode 100644 tests/auto/corelib/plugin/qlibrary/archlib/CMakeLists.txt create mode 100644 tests/auto/corelib/plugin/qlibrary/archlib/mylib.c diff --git a/src/corelib/plugin/qlibrary_unix.cpp b/src/corelib/plugin/qlibrary_unix.cpp index 479c08f2afe..d05a5fa0ac8 100644 --- a/src/corelib/plugin/qlibrary_unix.cpp +++ b/src/corelib/plugin/qlibrary_unix.cpp @@ -169,8 +169,10 @@ bool QLibraryPrivate::load_sys() // add ".avx2" to each suffix in the list transform(suffixes, [](QString *s) { s->append(".avx2"_L1); }); } else { - // prepend "haswell/" to each prefix in the list - transform(prefixes, [](QString *s) { s->prepend("haswell/"_L1); }); +# ifdef __GLIBC__ + // prepend "glibc-hwcaps/x86-64-v3/" to each prefix in the list + transform(prefixes, [](QString *s) { s->prepend("glibc-hwcaps/x86-64-v3/"_L1); }); +# endif } } #endif diff --git a/tests/auto/corelib/plugin/qlibrary/CMakeLists.txt b/tests/auto/corelib/plugin/qlibrary/CMakeLists.txt index b8f4af5aa84..4ee7e6b06ef 100644 --- a/tests/auto/corelib/plugin/qlibrary/CMakeLists.txt +++ b/tests/auto/corelib/plugin/qlibrary/CMakeLists.txt @@ -7,6 +7,7 @@ if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT) find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST) endif() +add_subdirectory(archlib) add_subdirectory(lib) add_subdirectory(lib2) add_subdirectory(tst) diff --git a/tests/auto/corelib/plugin/qlibrary/archlib/CMakeLists.txt b/tests/auto/corelib/plugin/qlibrary/archlib/CMakeLists.txt new file mode 100644 index 00000000000..329e8d7202d --- /dev/null +++ b/tests/auto/corelib/plugin/qlibrary/archlib/CMakeLists.txt @@ -0,0 +1,38 @@ +# Copyright (C) 2022 The Qt Company Ltd. +# Copyright (C) 2024 Intel Corporation. +# SPDX-License-Identifier: BSD-3-Clause + +if (UNIX) + qt_internal_add_cmake_library(tst_qlibrary_myarchlib + SHARED + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../" + SOURCES + mylib.c + LIBRARIES + Qt::Core + ) + + set_target_properties(tst_qlibrary_myarchlib PROPERTIES + OUTPUT_NAME myarchlib + VERSION 1.0.0 + SOVERSION 1 + ) + + if ("${TEST_architecture_arch}" STREQUAL "x86_64") + qt_internal_add_cmake_library(tst_qlibrary_myarchlib_x86-64-v3 + DEFINES + ARCH="x86-64-v3" + SHARED + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../glibc-hwcaps/x86-64-v3" + SOURCES + mylib.c + LIBRARIES + Qt::Core + ) + set_target_properties(tst_qlibrary_myarchlib_x86-64-v3 PROPERTIES + OUTPUT_NAME myarchlib + VERSION 1.0.0 + SOVERSION 1 + ) + endif() +endif() diff --git a/tests/auto/corelib/plugin/qlibrary/archlib/mylib.c b/tests/auto/corelib/plugin/qlibrary/archlib/mylib.c new file mode 100644 index 00000000000..5d9fe081cbb --- /dev/null +++ b/tests/auto/corelib/plugin/qlibrary/archlib/mylib.c @@ -0,0 +1,13 @@ +// Copyright (C) 2024 Intel Corporation. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include + +Q_DECL_EXPORT const char *archname() +{ +#ifdef ARCH + return ARCH; +#else + return ""; +#endif +} diff --git a/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp b/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp index 72957374fab..5ae49ff1707 100644 --- a/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp +++ b/tests/auto/corelib/plugin/qlibrary/tst_qlibrary.cpp @@ -63,6 +63,8 @@ # define PREFIX "lib" #endif +using namespace Qt::StringLiterals; + QT_FORWARD_DECLARE_CLASS(QLibrary) class tst_QLibrary : public QObject { @@ -97,6 +99,8 @@ private slots: void version_data(); void version(); void loadTwoVersions(); + void archSpecificVersion_data(); + void archSpecificVersion(); void setFileNameAndVersionTwice(); void setFileNameAndVersionAfterFailedLoad_data() { version_data(); } void setFileNameAndVersionAfterFailedLoad(); @@ -229,6 +233,42 @@ void tst_QLibrary::loadTwoVersions() lib1.unload(); } +void tst_QLibrary::archSpecificVersion_data() +{ +#if !defined(__GLIBC__) || !defined(Q_PROCESSOR_X86_64) + QSKIP("Test not applicable on this platform"); +#endif + + QTest::addColumn("lib"); + QTest::addColumn("version"); + + for (const char *prefix : { "", PREFIX }) { + QString libname = prefix + u"myarchlib"_s; + QTest::addRow("%s v1", qPrintable(libname)) << libname << 1; + QTest::addRow("%s.so.1", qPrintable(libname)) << (libname + SUFFIX + ".1") << -1; + } +} + +void tst_QLibrary::archSpecificVersion() +{ + QFETCH(QString, lib); + QFETCH(int, version); + QString expectedArch; + +#if defined(__GLIBC__) && defined(Q_PROCESSOR_X86_64) + if (__builtin_cpu_supports("avx2") && __builtin_cpu_supports("fma")) + expectedArch = "x86-64-v3"; +#endif + + QString appDir = directory; + QLibrary library(appDir + QLatin1Char('/') + lib, version); + QVERIFY2(library.load(), qPrintable(library.errorString())); + + auto archfunction = (const char *(*)())library.resolve("archname"); + QVERIFY(archfunction); + QCOMPARE(archfunction(), expectedArch); +} + void tst_QLibrary::setFileNameAndVersionTwice() { #if defined(Q_OS_ANDROID) || defined(Q_OS_WIN) diff --git a/tests/auto/corelib/plugin/qpluginloader/theplugin/CMakeLists.txt b/tests/auto/corelib/plugin/qpluginloader/theplugin/CMakeLists.txt index dfce9d6a524..8151f80db36 100644 --- a/tests/auto/corelib/plugin/qpluginloader/theplugin/CMakeLists.txt +++ b/tests/auto/corelib/plugin/qpluginloader/theplugin/CMakeLists.txt @@ -30,3 +30,21 @@ if (UNIX AND NOT APPLE) target_compile_options(theoldplugin PRIVATE -O0 -g3) endif() +if (UNIX AND "${TEST_architecture_arch}" STREQUAL "x86_64") + qt_internal_add_cmake_library(theplugin_x86-64-v3 + DEFINES + ARCH="x86-64-v3" + MODULE + INSTALL_DIRECTORY "${INSTALL_TESTSDIR}/tst_qpluginloader/bin" + OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/../bin" + SOURCES + theplugin.cpp theplugin.h + LIBRARIES + Qt::Core + ) + qt_autogen_tools_initial_setup(theplugin_x86-64-v3) + set_target_properties(theplugin_x86-64-v3 PROPERTIES + OUTPUT_NAME theplugin + SUFFIX "${CMAKE_SHARED_LIBRARY_SUFFIX}.avx2" + ) +endif() diff --git a/tests/auto/corelib/plugin/qpluginloader/theplugin/plugininterface.h b/tests/auto/corelib/plugin/qpluginloader/theplugin/plugininterface.h index 3fd6c384a4f..a7682fef508 100644 --- a/tests/auto/corelib/plugin/qpluginloader/theplugin/plugininterface.h +++ b/tests/auto/corelib/plugin/qpluginloader/theplugin/plugininterface.h @@ -8,6 +8,7 @@ struct PluginInterface { virtual ~PluginInterface() {} virtual QString pluginName() const = 0; + virtual const char *architectureName() const { return ""; }; }; QT_BEGIN_NAMESPACE diff --git a/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.cpp b/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.cpp index bfa45c7c484..69cc37dad83 100644 --- a/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.cpp +++ b/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.cpp @@ -1,4 +1,5 @@ // Copyright (C) 2016 The Qt Company Ltd. +// Copyright (C) 2024 Intel Corporation. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only #include #include "theplugin.h" @@ -9,6 +10,15 @@ QString ThePlugin::pluginName() const return QLatin1String("Plugin ok"); } +const char *ThePlugin::architectureName() const +{ +#ifdef ARCH + return ARCH; +#else + return ""; +#endif +} + static int pluginVariable = 0xc0ffee; extern "C" Q_DECL_EXPORT int *pointerAddress() { diff --git a/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.h b/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.h index a6b7e4a0839..9c8d5177af2 100644 --- a/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.h +++ b/tests/auto/corelib/plugin/qpluginloader/theplugin/theplugin.h @@ -15,6 +15,7 @@ class ThePlugin : public QObject, public PluginInterface public: virtual QString pluginName() const override; + const char *architectureName() const override; }; #endif // THEPLUGIN_H diff --git a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp index 4ff93f9de49..46ec5f693d9 100644 --- a/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp +++ b/tests/auto/corelib/plugin/qpluginloader/tst_qpluginloader.cpp @@ -201,6 +201,7 @@ private slots: void loadCorruptElfOldPlugin(); # endif #endif + void archSpecificVersion(); void loadMachO_data(); void loadMachO(); void relativePath(); @@ -850,6 +851,26 @@ void tst_QPluginLoader::loadCorruptElfOldPlugin() # endif // Qt 7 #endif // Q_OF_ELF +void tst_QPluginLoader::archSpecificVersion() +{ +#if !defined(QT_SHARED) + QSKIP("This test requires Qt to create shared libraries."); +#endif + QPluginLoader loader(sys_qualifiedLibraryName("theplugin")); + QVERIFY2(loader.load(), qPrintable(loader.errorString())); + + QString expectedArch; +#if defined(Q_PROCESSOR_X86_64) && defined(Q_OS_UNIX) && !defined(Q_OS_DARWIN) + if (__builtin_cpu_supports("avx2") && __builtin_cpu_supports("fma")) + expectedArch = "x86-64-v3"; +#endif + + PluginInterface* theplugin = qobject_cast(loader.instance()); + QVERIFY(theplugin); + QCOMPARE(theplugin->architectureName(), expectedArch); + QVERIFY(loader.unload()); +} + void tst_QPluginLoader::loadMachO_data() { #if defined(QT_BUILD_INTERNAL) && defined(Q_OF_MACH_O)