qversiontagging: Add Windows support

Details in the comment in the header. I haven't recently tested with
MSVC, but I designed this originally in 2015 when I did have access to
Visual Studio so I know it works (I've confirmed with Godbolt).

With MinGW, this produces assembly:

        .globl  qt_version_tag_used
        .section        .qtversion,"dr"
        .linkonce same_size
        .align 16
qt_version_tag_used:
        .quad   __imp_qt_version_tag_6_4
        .quad   394240

394240 is 0x060400. The DLL import table contains:

 0015f03c       0015f294 00000000 00000000 00165478 0016012c

        DLL Name: Qt6Core.t.dll
        vma:  Hint/Ord Member-Name Bound-To
        161318      7  _Z11qt_assert_xPKcS0_S0_i
...
        164cf8   6073  qt_version_tag_6_4

Change-Id: I42e7ef1a481840699a8dffff1404f45b13f60ba6
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@gmx.de>
Reviewed-by: Kai Koehne <kai.koehne@qt.io>
bb10
Thiago Macieira 2015-09-17 20:20:52 -07:00
parent b2edef557c
commit 13ad42c679
2 changed files with 113 additions and 13 deletions

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 Intel Corporation.
** Copyright (C) 2022 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -38,6 +38,7 @@
****************************************************************************/
#include "qglobal.h"
#include "qversiontagging.h"
#define SYM QT_MANGLE_NAMESPACE(qt_version_tag)
//#define SSYM QT_STRINGIFY(SYM)
@ -47,11 +48,18 @@
Q_CORE_EXPORT extern const char sym ## _ ## m ## _ ## n = 0; \
asm(".symver " QT_STRINGIFY(sym) "_" QT_STRINGIFY(m) "_" QT_STRINGIFY(n) ", " \
QT_STRINGIFY(sym) separator "Qt_" QT_STRINGIFY(m) "." QT_STRINGIFY(n))
#elif defined(Q_OS_WIN)
# define make_versioned_symbol2(sym, m, n, separator) \
Q_CORE_EXPORT extern const char sym ## _ ## m ## _ ## n = 0;
#else
# define make_versioned_symbol2(sym, m, n, separator)
#endif
#define make_versioned_symbol(sym, m, n, separator) make_versioned_symbol2(sym, m, n, separator)
QT_BEGIN_NAMESPACE
extern "C" {
#if QT_VERSION_MINOR > 0
make_versioned_symbol(SYM, QT_VERSION_MAJOR, 0, "@");
@ -108,3 +116,7 @@ make_versioned_symbol(SYM, QT_VERSION_MAJOR, 15, "@");
// the default version:
make_versioned_symbol(SYM, QT_VERSION_MAJOR, QT_VERSION_MINOR, "@@");
}
static_assert(std::is_trivially_destructible_v<QtPrivate::QVersionTag>);
QT_END_NAMESPACE

View File

@ -1,6 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2016 Intel Corporation.
** Copyright (C) 2022 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -46,22 +46,93 @@
QT_BEGIN_NAMESPACE
/*
* Ugly hack warning and explanation:
* Explanation
*
* This file causes all ELF modules, be they libraries or applications, to use the
* qt_version_tag symbol that is present in QtCore. Such symbol is versioned,
* so the linker will automatically pull the current Qt version and add it to
* the ELF header of the library/application. The assembly produces one section
* called ".qtversion" containing two 32-bit values. The first is a
* relocation to the qt_version_tag symbol (which is what causes the ELF
* version to get used). The second value is the current Qt version at the time
* of compilation.
* This file causes all libraries, plugins, and applications that #include this
* file to automatically pull in a symbol found in QtCore that encodes the
* current Qt version number at the time of compilation. The relocation is
* designed so that it's impossible for the dynamic linker to perform lazy
* binding. Instead, it must resolve at load time or fail. That way, attempting
* to load such a library or plugin while an older QtCore is loaded will fail.
* Similarly, if an older QtCore is found when launching an application, the
* application will fail to launch.
*
* It's also possible to inspect which version is required by decoding the
* .qtversion section. The second pointer-sized variable is the required
* version, for example, for Qt 6.4.1:
*
* Hex dump of section [18] '.qtversion', 16 bytes at offset 0x1ee48:
* 0x00000000 b0ffffff ffffffff 01040600 00000000 ................
* ^^^^^^^^ ^^^^^^^^
*
* There will only be one copy of the section in the output library or application.
*
* This functionality can be disabled by defining QT_NO_VERSION_TAGGING. It's
* disabled if Qt was built statically.
*
* Windows notes:
*
* On Windows, the address of a __declspec(dllimport) variable is not a
* constant expression, unlike Unix systems. So we instead use the address of
* the import variable, which is created by prefixing the external name with
* "__imp_". Using that variable causes an import of the corresponding symbol
* from QtCore DLL.
*
* With MinGW (GCC and Clang), we use a C++17 inline variable, so the compiler
* and linker automatically merge the variables. The "used" __attribute__
* tells the compiler to always emit that variable, whether it's used or not.
*
* MSVC has no equivalent to that attribute, so instead we create an extern
* const variable and tell the linker to merge them all via
* __declspec(selectany).
*
* ELF notes (Linux, FreeBSD):
*
* The symbol in question is simply "qt_version_tag" in both QtCore and in
* this ELF module, but it has an ELF version attached to it (see
* qversiontagging.cpp and QtFlagHandlingHelpers.cmake). That way, the error
* message from the dynamic linker will say it can't find version "Qt_6.x".
*
* This is currently implemented only for x86 by way of inline assembly. We
* inform the the linker to merge all TUs' .qtversion sections by way of the
* "comdat" attribute in the .section directive. The first pointer-sized
* variable is a GOT reference to qt_version_tag (that is: the address of
* qt_version_tag is stored in the .got section and the linker inserts the
* displacement to that pointer).
*/
namespace QtPrivate {
struct QVersionTag
{
const void *symbol;
quintptr version;
constexpr QVersionTag(const void *sym, int currentVersion = QT_VERSION)
: symbol(sym), version(currentVersion)
{}
};
}
#if defined(QT_BUILD_CORE_LIB) || defined(QT_BOOTSTRAPPED) || defined(QT_NO_VERSION_TAGGING) || defined(QT_STATIC)
// don't make tags in QtCore, bootstrapped systems or if the user asked not to
#elif defined(Q_OS_WIN)
# ifdef _WIN64
// 64-bit calling convention does not prepend a _
# define QT_MANGLE_IMPORT_PREFIX __imp_
# else
// 32-bit convention does prepend a _
# define QT_MANGLE_IMPORT_PREFIX _imp__
# endif
# ifdef Q_CC_MSVC
# pragma section(".qtversion",read,shared)
# define QT_VERSION_TAG_SECTION __declspec(allocate(".qtversion"))
# define QT_VERSION_TAG_ATTRIBUTE __declspec(selectany) extern const
# else
# define QT_VERSION_TAG_ATTRIBUTE __attribute__((used)) constexpr inline
# endif
# define QT_VERSION_TAG2(sym, imp) \
extern "C" const char * const imp; \
QT_VERSION_TAG_ATTRIBUTE QT_VERSION_TAG_SECTION QtPrivate::QVersionTag sym ## _used(&imp)
# define QT_VERSION_TAG(sym, imp) QT_VERSION_TAG2(sym, imp)
#elif defined(Q_CC_GNU) && !defined(Q_OS_ANDROID)
# if defined(Q_PROCESSOR_X86) && (defined(Q_OS_LINUX) || defined(Q_OS_FREEBSD_KERNEL))
# if defined(Q_PROCESSOR_X86_64) && QT_POINTER_SIZE == 8 // x86-64 64-bit
@ -69,7 +140,7 @@ QT_BEGIN_NAMESPACE
# else // x86 or x86-64 32-bit (x32)
# define QT_VERSION_TAG_RELOC(sym) ".long " QT_STRINGIFY(QT_MANGLE_NAMESPACE(sym)) "@GOT\n"
# endif
# define QT_VERSION_TAG(sym) \
# define QT_VERSION_TAG(sym, imp) \
asm ( \
".section .qtversion, \"aG\", @progbits, " QT_STRINGIFY(QT_MANGLE_NAMESPACE(sym)) ", comdat\n" \
".align 8\n" \
@ -81,8 +152,25 @@ QT_BEGIN_NAMESPACE
# endif
#endif
#ifdef Q_OF_ELF
# define QT_VERSION_TAG_SYMBOL(prefix, sym, m, n) sym
#else
# define QT_VERSION_TAG_SYMBOL2(prefix, sym, m, n) prefix ## sym ## _ ## m ## _ ## n
# define QT_VERSION_TAG_SYMBOL(prefix, sym, m, n) QT_VERSION_TAG_SYMBOL2(prefix, sym, m, n)
#endif
#if defined(QT_VERSION_TAG)
QT_VERSION_TAG(qt_version_tag);
# ifndef QT_VERSION_TAG_SECTION
# define QT_VERSION_TAG_SECTION __attribute__((section(".qtversion")))
# endif
# define QT_MANGLED_VERSION_TAG_IMPORT QT_VERSION_TAG_SYMBOL(QT_MANGLE_IMPORT_PREFIX, qt_version_tag, QT_VERSION_MAJOR, QT_VERSION_MINOR)
# define QT_MANGLED_VERSION_TAG QT_VERSION_TAG_SYMBOL(, QT_MANGLE_NAMESPACE(qt_version_tag), QT_VERSION_MAJOR, QT_VERSION_MINOR)
QT_VERSION_TAG(QT_MANGLED_VERSION_TAG, QT_MANGLED_VERSION_TAG_IMPORT);
# undef QT_MANGLED_VERSION_TAG
# undef QT_MANGLED_VERSION_TAG_IMPORT
# undef QT_VERSION_TAG_SECTION
#endif
QT_END_NAMESPACE