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 <fabian.kosmale@qt.io>
This commit is contained in:
Thiago Macieira 2024-06-17 14:22:13 -07:00
parent 01d4be4a83
commit a1db295912
10 changed files with 147 additions and 2 deletions

View File

@ -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

View File

@ -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)

View File

@ -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()

View File

@ -0,0 +1,13 @@
// Copyright (C) 2024 Intel Corporation.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <qglobal.h>
Q_DECL_EXPORT const char *archname()
{
#ifdef ARCH
return ARCH;
#else
return "";
#endif
}

View File

@ -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<QString>("lib");
QTest::addColumn<int>("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)

View File

@ -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()

View File

@ -8,6 +8,7 @@
struct PluginInterface {
virtual ~PluginInterface() {}
virtual QString pluginName() const = 0;
virtual const char *architectureName() const { return ""; };
};
QT_BEGIN_NAMESPACE

View File

@ -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 <QtCore/QString>
#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()
{

View File

@ -15,6 +15,7 @@ class ThePlugin : public QObject, public PluginInterface
public:
virtual QString pluginName() const override;
const char *architectureName() const override;
};
#endif // THEPLUGIN_H

View File

@ -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<PluginInterface*>(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)