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:
parent
8b34296e6a
commit
9b8493314d
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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))
|
||||
|
@ -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.
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
Loading…
x
Reference in New Issue
Block a user