qt example plugandpaint 插件 动态库 pnp_extrafiltersd.dll无法加载问题

68 篇文章 11 订阅

使用版本windows qt5.12.0+vs2015编译器。

运行plugandpaint工程的时候发现pnp_extrafiltersd.dll在load的时候失败了,经过调试,发现qlibrary.cpp中的findPatternUnloaded()的qt_find_pattern()无法通过。(release 版的pnp_extrafilters.dll是可以通过的,也不存在下面的问题)

qt_find_pattern调用堆栈
//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项,如下:

moc_extrafilters.cpp的metadata

 正常来讲,metadata中的内容应该会存放到dll中,但pnp_extrafiltersd.dll出现异常。对比如下:

正常的dll的metadata的内容(plugins/audio/qtaudio_wasapi.dll)
异常的dll的metadata的内容(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;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值