Qt6: Fix uninitialized meta objects on Windows

Windows has a problem relating to cross-DLL variable relocations: they
are not supported. Since QMetaObject's link to the parent class is done
via a pointer, every QMetaObject in a DLL or in the EXE that derives
from a class from another DLL (such as QObject) will be dynamically
initialized.

This commit changes the meta object pointers in QMetaObject::d from raw
pointers to a wrapper class SuperData, which is almost entirely source-
compatible with the pointer itself. On all systems except for Windows
with Qt 6, it's binary compatible with the current implementation.

But for Windows with Qt 6, this commit will store both the raw pointer
and a pointer to a function that returns the QMetaObject, with one of
them non-null only. For all meta objects constructed by moc, we store
the function pointer, which allows the staticMetaObject to be statically
intialized. For dynamic meta objects (QMetaObjectBuilder, QtDBus, QtQml,
ActiveQt), we'll store the actual raw pointer.

[ChangeLog][QtCore][QMetaObject] Some internal members of the
QMetaObject class have changed types. Those members are not public API
and thus should not cause source incompatibilities.

The macro QT_NO_DATA_RELOCATION existed in Qt 4 but was called
Q_NO_DATA_RELOCATION and only applied to Symbian. It was removed in
commit 24a72c4efa929648d3afd95b3c269a95ecf46e57 ("qglobal: Remove
symbian specific features").

Task-number: QTBUG-38876
Fixes: QTBUG-69963
Change-Id: Id92f4a61915b49ddaee6fffd14ae1cf615525e92
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
This commit is contained in:
Thiago Macieira 2017-03-21 22:26:09 -07:00
parent 8b34296e6a
commit 9b8493314d
8 changed files with 66 additions and 25 deletions

View File

@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2019 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -612,7 +612,8 @@ using qsizetype = QIntegerForSizeof<std::size_t>::Signed;
# define Q_ALWAYS_INLINE inline
#endif
#if defined(Q_CC_GNU) && defined(Q_OS_WIN)
#if defined(Q_CC_GNU) && defined(Q_OS_WIN) && !defined(QT_NO_DATA_RELOCATION)
// ### Qt6: you can remove me
# define QT_INIT_METAOBJECT __attribute__((init_priority(101)))
#else
# define QT_INIT_METAOBJECT

View File

@ -1,6 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2019 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -180,6 +181,12 @@
#if defined(Q_OS_WIN32) || defined(Q_OS_WIN64) || defined(Q_OS_WINRT)
# define Q_OS_WINDOWS
# define Q_OS_WIN
# if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
// On Windows, pointers to dllimport'ed variables are not constant expressions,
// so to keep to certain initializations (like QMetaObject) constexpr, we need
// to use functions instead.
# define QT_NO_DATA_RELOCATION
# endif
#endif
#if defined(Q_OS_WIN)

View File

@ -953,7 +953,7 @@ static const QMetaObject *QMetaObject_findMetaObject(const QMetaObject *self, co
return self;
if (self->d.relatedMetaObjects) {
Q_ASSERT(priv(self->d.data)->revision >= 2);
const QMetaObject * const *e = self->d.relatedMetaObjects;
const auto *e = self->d.relatedMetaObjects;
if (e) {
while (*e) {
if (const QMetaObject *m =QMetaObject_findMetaObject((*e), name))

View File

@ -747,7 +747,7 @@ void QMetaObjectBuilder::addMetaObject
if ((members & RelatedMetaObjects) != 0) {
Q_ASSERT(priv(prototype->d.data)->revision >= 2);
const QMetaObject * const *objects = prototype->d.relatedMetaObjects;
const auto *objects = prototype->d.relatedMetaObjects;
if (objects) {
while (*objects != 0) {
addRelatedMetaObject(*objects);
@ -1464,16 +1464,16 @@ static int buildMetaObject(QMetaObjectBuilderPrivate *d, char *buf,
// Create the relatedMetaObjects block if we need one.
if (d->relatedMetaObjects.size() > 0) {
ALIGN(size, QMetaObject *);
const QMetaObject **objects =
reinterpret_cast<const QMetaObject **>(buf + size);
using SuperData = QMetaObject::SuperData;
ALIGN(size, SuperData);
auto objects = reinterpret_cast<SuperData *>(buf + size);
if (buf) {
meta->d.relatedMetaObjects = objects;
for (index = 0; index < d->relatedMetaObjects.size(); ++index)
objects[index] = d->relatedMetaObjects[index];
objects[index] = 0;
objects[index] = nullptr;
}
size += sizeof(QMetaObject *) * (d->relatedMetaObjects.size() + 1);
size += sizeof(SuperData) * (d->relatedMetaObjects.size() + 1);
}
// Align the final size and return it.

View File

@ -1,7 +1,7 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2016 Intel Corporation.
** Copyright (C) 2019 The Qt Company Ltd.
** Copyright (C) 2019 Intel Corporation.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the QtCore module of the Qt Toolkit.
@ -572,13 +572,42 @@ struct Q_CORE_EXPORT QMetaObject
int static_metacall(Call, int, void **) const;
static int metacall(QObject *, Call, int, void **);
template <const QMetaObject &MO> static constexpr const QMetaObject *staticMetaObject()
{
return &MO;
}
struct SuperData {
const QMetaObject *direct;
SuperData() = default;
constexpr SuperData(std::nullptr_t) : direct(nullptr) {}
constexpr SuperData(const QMetaObject *mo) : direct(mo) {}
constexpr const QMetaObject *operator->() const { return operator const QMetaObject *(); }
#ifdef QT_NO_DATA_RELOCATION
using Getter = const QMetaObject *(*)();
Getter indirect = nullptr;
constexpr SuperData(Getter g) : direct(nullptr), indirect(g) {}
constexpr operator const QMetaObject *() const
{ return indirect ? indirect() : direct; }
template <const QMetaObject &MO> static constexpr SuperData link()
{ return SuperData(QMetaObject::staticMetaObject<MO>); }
#else
constexpr operator const QMetaObject *() const
{ return direct; }
template <const QMetaObject &MO> static constexpr SuperData link()
{ return SuperData(QMetaObject::staticMetaObject<MO>()); }
#endif
};
struct { // private data
const QMetaObject *superdata;
SuperData superdata;
const QByteArrayData *stringdata;
const uint *data;
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
StaticMetacallFunction static_metacall;
const QMetaObject * const *relatedMetaObjects;
const SuperData *relatedMetaObjects;
void *extradata; //reserved for future use
} d;

View File

@ -518,10 +518,15 @@ void Generator::generateCode()
}
}
//
// Generate meta object link to parent meta objects
//
if (!extraList.isEmpty()) {
fprintf(out, "static const QMetaObject * const qt_meta_extradata_%s[] = {\n ", qualifiedClassNameIdentifier.constData());
fprintf(out, "static const QMetaObject::SuperData qt_meta_extradata_%s[] = {\n",
qualifiedClassNameIdentifier.constData());
for (int i = 0; i < extraList.count(); ++i) {
fprintf(out, " &%s::staticMetaObject,\n", extraList.at(i).constData());
fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", extraList.at(i).constData());
}
fprintf(out, " nullptr\n};\n\n");
}
@ -537,7 +542,7 @@ void Generator::generateCode()
if (isQObject)
fprintf(out, " nullptr,\n");
else if (cdef->superclassList.size() && (!cdef->hasQGadget || knownGadgets.contains(purestSuperClass)))
fprintf(out, " &%s::staticMetaObject,\n", purestSuperClass.constData());
fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", purestSuperClass.constData());
else
fprintf(out, " nullptr,\n");
fprintf(out, " qt_meta_stringdata_%s.data,\n"

View File

@ -1322,8 +1322,8 @@ bool tst_QMetaObjectBuilder::sameMetaObject
return false;
}
const QMetaObject * const *objects1 = meta1->d.relatedMetaObjects;
const QMetaObject * const *objects2 = meta2->d.relatedMetaObjects;
const auto *objects1 = meta1->d.relatedMetaObjects;
const auto *objects2 = meta2->d.relatedMetaObjects;
if (objects1 && !objects2)
return false;
if (objects2 && !objects1)
@ -1391,7 +1391,7 @@ private:
};
QMetaObject TestObject::staticMetaObject = {
{ 0, 0, 0, 0, 0, 0 }
{ nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }
};
TestObject::TestObject(QObject *parent)

View File

@ -1053,7 +1053,7 @@ void tst_Moc::testExtraDataForEnum()
const QMetaObject *mobjUser = &EnumUserClass::staticMetaObject;
QCOMPARE(mobjUser->enumeratorCount(), 0);
const QMetaObject * const *objects = mobjUser->d.relatedMetaObjects;
const auto *objects = mobjUser->d.relatedMetaObjects;
QVERIFY(objects);
QCOMPARE(objects[0], mobjSource);
QVERIFY(!objects[1]);
@ -3579,10 +3579,9 @@ namespace QTBUG32933_relatedObjectsDontIncludeItself {
void tst_Moc::QTBUG32933_relatedObjectsDontIncludeItself()
{
const QMetaObject *mo = &QTBUG32933_relatedObjectsDontIncludeItself::NS::Obj::staticMetaObject;
const QMetaObject * const *objects = mo->d.relatedMetaObjects;
const auto *objects = mo->d.relatedMetaObjects;
// the related objects should be empty because the enums is in the same object.
QVERIFY(!objects);
}
class UnrelatedClass : public QObject
@ -3688,7 +3687,7 @@ void tst_Moc::relatedMetaObjectsNameConflict()
// load all specified metaobjects int a set
QSet<const QMetaObject*> dependency;
const QMetaObject *const *i = dependingObject->d.relatedMetaObjects;
const auto *i = dependingObject->d.relatedMetaObjects;
while (*i) {
dependency.insert(*i);
++i;