From 40c8bc42026a8cf118ab60920314d84856c397a6 Mon Sep 17 00:00:00 2001 From: Thiago Macieira Date: Wed, 9 Feb 2022 12:50:36 -0800 Subject: [PATCH] qversiontagging: Use C++17 inline variables also on Unix C++17 inline variables allow us to declare a variable that shall be merged before linking, which replaces the need for the "comdat" part of the inline assembly. The GCC attribute "used" tells the compiler not to discard this variable, like the MinGW case. Additionally, the "retain" attribute (where supported) tells both the compiler and linker not to discard, allowing an intermediary, static library to keep this definition. This enables support for OSes besides FreeBSD and Linux, where it was previously available. For example, on macOS: $ nm libexec/rcc | grep qt_version_tag U _qt_version_tag_6_4 00000001000ec608 s _qt_version_tag_6_4_use On Linux, the assembly output before this change (Clang 13) was: .section .qtversion,"aG",@progbits,qt_version_tag,comdat .p2align 3 .quad qt_version_tag@GOT .long 394240 .p2align 3 After this change: .hidden qt_version_tag_use # @qt_version_tag_use .type qt_version_tag_use,@object .section .qtversion,"aGwR",@progbits,qt_version_tag_use,comdat .weak qt_version_tag_use .p2align 3 qt_version_tag_use: .quad qt_version_tag .quad 394240 # 0x60400 .size qt_version_tag_use, 16 The notable changes here are that there is a symbol and that the section is writable. The latter is required because we store the pointer to the qt_version_tag variable instead of just an offset in the GOT. The total number of relocations in the resulting binary remains the same. We've actually shrunk the binary by one pointer size. Change-Id: I74249c52dc02478ba93cfffd16d23951a6bcd784 Reviewed-by: Oswald Buddenhagen Reviewed-by: Thiago Macieira --- src/corelib/global/qversiontagging.cpp | 7 ++---- src/corelib/global/qversiontagging.h | 32 +++++++++++++++++--------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/src/corelib/global/qversiontagging.cpp b/src/corelib/global/qversiontagging.cpp index 8f33c8af8c..e79cf9f1b5 100644 --- a/src/corelib/global/qversiontagging.cpp +++ b/src/corelib/global/qversiontagging.cpp @@ -43,18 +43,15 @@ #define SYM QT_MANGLE_NAMESPACE(qt_version_tag) //#define SSYM QT_STRINGIFY(SYM) -#if defined(Q_CC_GNU) && defined(Q_OF_ELF) && !defined(Q_OS_ANDROID) +#if defined(Q_CC_GNU) && defined(Q_OF_ELF) # define make_versioned_symbol2(sym, m, n, separator) \ 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) +#else # 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) diff --git a/src/corelib/global/qversiontagging.h b/src/corelib/global/qversiontagging.h index 721835cb85..e8408c5796 100644 --- a/src/corelib/global/qversiontagging.h +++ b/src/corelib/global/qversiontagging.h @@ -86,19 +86,16 @@ QT_BEGIN_NAMESPACE * const variable and tell the linker to merge them all via * __declspec(selectany). * - * ELF notes (Linux, FreeBSD): + * Unix notes: * - * 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". + * On Unix, we use the same C++17 inline variable solution as MinGW, but we + * don't need the "__imp_" trick. * - * 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). + * Additionally, on ELF systems like Linux and 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". */ namespace QtPrivate { @@ -133,6 +130,19 @@ struct QVersionTag 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) && __has_attribute(used) +# ifdef Q_OS_DARWIN +# define QT_VERSION_TAG_SECTION __attribute__((section("__DATA,.qtversion"))) +# endif +# if __has_attribute(retain) +# define QT_VERSION_TAG_ATTRIBUTE __attribute__((visibility("hidden"), retain, used)) +# else +# define QT_VERSION_TAG_ATTRIBUTE __attribute__((visibility("hidden"), used)) +# endif +# define QT_VERSION_TAG2(sym, imp) \ + extern "C" Q_DECL_IMPORT const char sym; \ + QT_VERSION_TAG_ATTRIBUTE QT_VERSION_TAG_SECTION constexpr inline QtPrivate::QVersionTag sym ## _use(&sym) +# 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