使用版本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<char *>(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;
}