summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThiago Macieira <thiago.macieira@intel.com>2015-09-17 20:20:52 -0700
committerThiago Macieira <thiago.macieira@intel.com>2022-03-14 18:26:09 -0800
commit13ad42c67988aab09bff0ee83903e1264d6048fe (patch)
tree400e3146a68d13631664051c3ad37c571f4b5fc7
parentb2edef557cf97ae9251be3675f8179a7d2298faa (diff)
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>
-rw-r--r--src/corelib/global/qversiontagging.cpp14
-rw-r--r--src/corelib/global/qversiontagging.h112
2 files changed, 113 insertions, 13 deletions
diff --git a/src/corelib/global/qversiontagging.cpp b/src/corelib/global/qversiontagging.cpp
index a0d15ae871..8f33c8af8c 100644
--- a/src/corelib/global/qversiontagging.cpp
+++ b/src/corelib/global/qversiontagging.cpp
@@ -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
diff --git a/src/corelib/global/qversiontagging.h b/src/corelib/global/qversiontagging.h
index 75c2e9df7e..721835cb85 100644
--- a/src/corelib/global/qversiontagging.h
+++ b/src/corelib/global/qversiontagging.h
@@ -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