使用版本windows qt5.12.0+vs2015编译器。
运行plugandpaint工程的时候发现pnp_extrafiltersd.dll在load的时候失败了,经过调试,发现qlibrary.cpp中的findPatternUnloaded()的qt_find_pattern()无法通过。(release 版的pnp_extrafilters.dll是可以通过的,也不存在下面的问题)
- //QtInstallDir\Qt5.12.0\5.12.0\Src\qtbase\src\corelib\plugin\qlibrary.cpp
- .....
- char pattern[] = "qTMETADATA ";
- pattern[0] = 'Q'; // Ensure the pattern "QTMETADATA" is not found in this library should QPluginLoader ever encounter it.
- const ulong plen = qstrlen(pattern);
-
- ......
-
- pos = qt_find_pattern(filedata, fdlen, pattern, plen); //找到二进制文件中的QTMETADATA
- if (pos > 0)
- hasMetaData = true;
- .....
- if (pos >= 0 && hasMetaData) {
- const char *data = filedata + pos;
- QString errMsg;
- QJsonDocument doc = qJsonFromRawLibraryMetaData(data, fdlen, &errMsg);//直接将二进制文件中的QTMETADATA后面的内容转成json格式,此处在debug模式下会报错
- if (doc.isNull()) {
- qWarning("Found invalid metadata in lib %s: %s",
- qPrintable(library), qPrintable(errMsg));
- } else {
- lib->metaData = doc.object();
- if (qt_debug_component())
- qWarning("Found metadata in lib %s, metadata=\n%s\n",
- library.toLocal8Bit().constData(), doc.toJson().constData());
- ret = !doc.isNull();
- }
- }
qt moc生成的moc_*.cpp有一个metadata项,如下:
正常来讲,metadata中的内容应该会存放到dll中,但pnp_extrafiltersd.dll出现异常。对比如下:
对比发现,pnp_extrafiltersd.dll的qt_pluginMetaData在遇到qPluginArchRequirements()的时候就被截断了,可以看到pnp_extrafiltersd.dll中qt_pluginMetaData的内容没有紧凑的存放在一起。qJsonFromRawLibraryMetaData函数在pnp_extrafiltersd.dll中找到“QTMETADATA !”之后无法将其后的二进制内容成功转换出正确的metadata的json内容。然而在release版中却没有这个现象,qt_pluginMetaData的内容在dll中全部紧凑的存放在一起,qJsonFromRawLibraryMetaData能正确对其进行解析。
解决方法:
1、先右键清除,将之前生成的obj等内容先清除掉,一定要做清除操作,否则残留的文件会对生成结果有影响。
2、在extrafilters.pro中加入:QMAKE_CXXFLAGS_DEBUG += -O1 (对VS 编译器debug模式设置O1优化,O1优化会在编译时将inline函数结果计算出来,否则程序会做一次函数调用,上面的现象就是因为编译器没有直接将qPluginArchRequirements()直接计算结果,而是做了一次函数调用操作,才导致字符数组qt_pluginMetaData的值没有在dll中紧凑排列,而导致后续的问题。debug默认不开启优化也就是O0,release默认开启最优优化,也就是O2优化)
inline 函数G++ 优化_Ruifei_yu的博客-CSDN博客
HMI-19-[Qt Release深度优化编译]开启-O优化编译_DreamLife.的博客-CSDN博客
下面是从qt中特意拷贝出识别qtplugin 的 dll的代码,有兴趣可以看一看,可以放到main中调试
- #include <qjsondocument.h>
- #include <qjsonvalue.h>
- #include <qjsonobject.h>
- #include <qjsonarray.h>
- #include <qcbormap.h>
- #include <qcborvalue.h>
- #include <qendian.h>
- #include <qplugin.h>
- #include <private/qplugin_p.h>
- #include <qpluginloader.h>
-
-
- QJsonObject metaData;
-
- static inline int metaDataSignatureLength()
- {
- return sizeof("QTMETADATA ") - 1;
- }
-
- static QJsonDocument jsonFromCborMetaData(const char *raw, qsizetype size, QString *errMsg)
- {
- // extract the keys not stored in CBOR
- int qt_metadataVersion = quint8(raw[0]);
- int qt_version = qFromBigEndian<quint16>(raw + 1);
- int qt_archRequirements = quint8(raw[3]);
- if (Q_UNLIKELY(raw[-1] != '!' || qt_metadataVersion != 0)) {
- *errMsg = QStringLiteral("Invalid metadata version");
- return QJsonDocument();
- }
-
- raw += 4;
- size -= 4;
- QByteArray ba = QByteArray::fromRawData(raw, int(size));
- QCborParserError err;
- QCborValue metadata = QCborValue::fromCbor(ba, &err);
-
- if (err.error != QCborError::NoError) {
- *errMsg = QLatin1String("Metadata parsing error: ") + err.error.toString();
- return QJsonDocument();
- }
-
- if (!metadata.isMap()) {
- *errMsg = QStringLiteral("Unexpected metadata contents");
- return QJsonDocument();
- }
-
- QJsonObject o;
- o.insert(QLatin1String("version"), qt_version << 8);
- o.insert(QLatin1String("debug"), bool(qt_archRequirements & 1));
- o.insert(QLatin1String("archreq"), qt_archRequirements);
-
- // convert the top-level map integer keys
- for (auto it : metadata.toMap()) {
- QString key;
- if (it.first.isInteger()) {
- switch (it.first.toInteger()) {
- #define CONVERT_TO_STRING(IntKey, StringKey, Description) \
- case int(IntKey): key = QStringLiteral(StringKey); break;
- QT_PLUGIN_FOREACH_METADATA(CONVERT_TO_STRING)
- #undef CONVERT_TO_STRING
-
- case int(QtPluginMetaDataKeys::Requirements):
- // special case: recreate the debug key
- o.insert(QLatin1String("debug"), bool(it.second.toInteger() & 1));
- key = QStringLiteral("archreq");
- break;
- }
- } else {
- key = it.first.toString();
- }
-
- if (!key.isEmpty())
- o.insert(key, it.second.toJsonValue());
- }
- return QJsonDocument(o);
- }
-
- QJsonDocument qJsonFromRawLibraryMetaData(const char *raw, qsizetype sectionSize, QString *errMsg)
- {
- raw += metaDataSignatureLength();
- sectionSize -= metaDataSignatureLength();
-
- #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
- if (Q_UNLIKELY(raw[-1] == ' ')) {
- // the size of the embedded JSON object can be found 8 bytes into the data (see qjson_p.h)
- uint size = qFromLittleEndian<uint>(raw + 8);
- // but the maximum size of binary JSON is 128 MB
- size = qMin(size, 128U * 1024 * 1024);
- // and it doesn't include the size of the header (8 bytes)
- size += 8;
- // finally, it can't be bigger than the file or section size
- size = qMin(sectionSize, qsizetype(size));
-
- QByteArray json(raw, size);
- return QJsonDocument::fromBinaryData(json);
- }
- #endif
-
- return jsonFromCborMetaData(raw, sectionSize, errMsg);
- }
-
- static qsizetype qt_find_pattern(const char *s, qsizetype s_len,
- const char *pattern, ulong p_len)
- {
- /*
- we search from the end of the file because on the supported
- systems, the read-only data/text segments are placed at the end
- of the file. HOWEVER, when building with debugging enabled, all
- the debug symbols are placed AFTER the data/text segments.
-
- what does this mean? when building in release mode, the search
- is fast because the data we are looking for is at the end of the
- file... when building in debug mode, the search is slower
- because we have to skip over all the debugging symbols first
- */
- if (!s || !pattern || qsizetype(p_len) > s_len)
- return -1;
-
- size_t i, hs = 0, hp = 0, delta = s_len - p_len;
-
- for (i = 0; i < p_len; ++i) {
- hs += s[delta + i];
- hp += pattern[i];
- }
- i = delta;
- for (;;) {
- if (hs == hp && qstrncmp(s + i, pattern, p_len) == 0)
- return i; // can't overflow, by construction
- if (i == 0)
- break;
- --i;
- hs -= s[i + p_len];
- hs += s[i];
- }
- return -1;
- }
- static bool findPatternUnloaded(const QString &library)
- {
- QFile file(library);
- if (!file.open(QIODevice::ReadOnly)) {
- return false;
- }
- // Files can be bigger than the virtual memory size on 32-bit systems, so
- // we limit to 512 MB there. For 64-bit, we allow up to 2^40 bytes.
- constexpr qint64 MaxMemoryMapSize =
- Q_INT64_C(1) << (sizeof(qsizetype) > 4 ? 40 : 29);
- QByteArray data;
- qsizetype fdlen = qMin(file.size(), MaxMemoryMapSize);
- const char *filedata = reinterpret_cast
(file.map(0, fdlen)); - if (filedata == 0) {
- // Try reading the data into memory instead (up to 64 MB).
- data = file.read(64 * 1024 * 1024);
- filedata = data.constData();
- fdlen = data.size();
- }
- /*
- ELF and Mach-O binaries with GCC have .qplugin sections.
- */
- bool hasMetaData = false;
- qsizetype pos = 0;
- char pattern[] = "qTMETADATA ";
- pattern[0] = 'T'; // Ensure the pattern "QTMETADATA" is not found in this library should QPluginLoader ever encounter it.
- const ulong plen = qstrlen(pattern);
- pos = qt_find_pattern(filedata, fdlen, pattern, plen);
- if (pos > 0)
- hasMetaData = true;
- bool ret = false;
- if (pos >= 0 && hasMetaData) {
- const char *data = filedata + pos;
- QString errMsg;
- QJsonDocument doc = qJsonFromRawLibraryMetaData(data, fdlen, &errMsg);
- if (doc.isNull()) {
- qWarning("Found invalid metadata in lib %s: %s",
- qPrintable(library), qPrintable(errMsg));
- } else {
- metaData = doc.object();
- ret = !doc.isNull();
- }
- }
- }
- int main()
- {
- findPatternUnloaded("E:/workspace/QtWork/build-plugandpaint-Desktop_Qt_5_12_0_MSVC2015_64bit-Debug/plugins/pnp_extrafiltersd.dll");
- return 0;
- }