如何使用交叉编译的构建调试 Qt dll 问题?

Posted

技术标签:

【中文标题】如何使用交叉编译的构建调试 Qt dll 问题?【英文标题】:How to debug Qt dll issues with cross compiled builds? 【发布时间】:2013-10-24 20:07:48 【问题描述】:

在交叉编译 Qt 5 应用程序(主机:Fedora 19/64 位,目标:Windows 32 位)后,我执行以下步骤来部署可执行文件:

$ DEST=/windows/testdir
$ cp /usr/i686-w64-mingw32/sys-root/mingw/bin/*.dll $DEST
$ mkdir $DEST/platforms
$ cp /usr/i686-w64-mingw32/sys-root/mingw/lib/qt5/plugins/platforms/qwindows.dll\
     $DEST/platforms
$ cp release/main.exe $DEST # the cross-compiled Qt5 binary

我在 Windows 上这样测试它:

say /windows is mounted on f:
start command prompt window
f:
cd testdir
main

我得到了:

无法加载平台插件“windows”。可用平台有:

Microsoft Visual C++ 运行时库 此应用程序已请求运行时以不寻常的方式终止它。 请联系应用程序的支持团队了解更多信息。

我不太相信第一条消息,因为:

a) 上述步骤过去有效(在同一个 Fedora 19 系统上执行)

b) 平台目录是 documented in the qt docs。

改变的是现在应用程序在对话框中包含了一些 PNG/JPG(通过 Qt 的资源文件系统读取,作为 QIcons)。

因此,我也复制了一些插件:

$ cp -r /usr/i686-w64-mingw32/sys-root/mingw/lib/qt5/plugins $DEST

这无助于解决上述问题。

结论

有没有办法调试这些动态运行时链接器问题?

我可以指示它以某种方式获得应用程序/链接器尝试加载的 dll 以及它的查找在哪里的输出吗? (以及他们失败的原因......)

例如这样的事情会很棒:

ldd: main.exe -> load of foo.dll in work-dir failed (no such file)
ldd: main.exe -> load of bar.dll in work-dir/platforms failed (wrong file format)
ldd: main.exe -> load of baz.dll in work-dir successful
...

编译步骤

我在 Fedora 19 上使用以下步骤进行交叉编译:

$ mingw32-qmake-qt5 main.pro -o win32.mf
$ mingw32-make -f win32.mf
$ # -> binary is created in release/main.exe

葡萄酒

出于测试目的,我查看了葡萄酒。这很有帮助,因为它在找不到 DLL 时会显示错误消息,例如:

$ wine $DEST/main.exe
err:module:import_dll Library libEGL.dll (which is needed by L"Z:\\usr\\i686-w64-mingw32\\sys-root\\mingw\\lib\\qt5\\plugins\\platforms\\qwindows.dll") not found
err:module:import_dll Library libjpeg-62.dll (which is needed by L"Z:\\usr\\i686-w64-mingw32\\sys-root\\mingw\\lib\\qt5\\plugins\\imageformats\\qjpeg.dll") not found

有趣的是,它直接在Z:\\usr\\i686-w64-mingw32\\sys-root\\mingw\\lib\\qt5\\下找到了平台库和需要的插件。

但是当所有需要的来自 /usr/i686-w64-mingw32/sys-root/mingw/bin/*.dll 的 DLL 都复制到 $DEST 时,wine 运行相同的 main.exe 就好了 - 在本机 Windows (7) 上我得到了上述错误框。

【问题讨论】:

使用依赖walker dependencywalker.com 检查平台插件DLL。 procmon technet.microsoft.com/en-us/sysinternals/bb896645.aspx 应该显示 LoadLibrary 调用以及搜索 DLL 的位置。 【参考方案1】:

您可以使用Dependency Walker 等工具检查单个 DLL 的依赖关系,Wine 用于快速检查编译主机上的启动,Process Monitor 以查看在进程运行期间访问了哪些目录/文件。

从 Qt 应用程序中调试输出库路径也很有意义,例如

int main(int argc, char **argv)                                         

  qDebug() << "Library paths: " << QApplication::libraryPaths();
  QApplication app(argc, argv);
  ...

我在本机窗口上得到以下输出:

Library paths:  ()

(要启用 qDebug() 语句 - 即使是发布二进制文件 - 您必须将 CONFIG += console 添加到您的 qmake 项目文件中。)

查看进程监视器的输出,似乎二进制文件没有尝试在其当前工作目录 (CWD) 或其基本目录中打开任何插件(平台或其他)。

当我扩展库路径时,二进制文件会在其 CWD 中找到所有需要的插件:

int main(int argc, char **argv)

  QApplication::addLibraryPath(QDir::currentPath());
  QApplication app(argc, argv);
  ...

我不知道这是否可以作为一种解决方法 - 也许应该做这样的事情。但Qt documentation 似乎暗示相反:

要部署应用程序,我们必须确保将相关的Qt DLL(对应应用程序中使用的Qt模块)和windows平台插件以及可执行文件复制到release子目录下的同一目录下。

完整的部署过程现在是:

$ cp /usr/i686-w64-mingw32/sys-root/mingw/bin/*.dll $DEST
# copying platforms, imageformats etc. plugin directories:
$ cp /usr/i686-w64-mingw32/sys-root/mingw/lib/qt5/plugins/* $DEST -r
$ cp release/main.exe $DEST

(根据您的编译主机上安装的软件包,您可能不需要复制所有 DLL - 使用 wine 很容易为所有需要的非插件 dll 启动定点迭代。)

缺少非平台插件不一定会中止程序启动 - 例如。如果没有 jpeg 插件,一些图标就不会显示。

【讨论】:

以上是关于如何使用交叉编译的构建调试 Qt dll 问题?的主要内容,如果未能解决你的问题,请参考以下文章

调试 Qt 项目文件?

QtCreator配置交叉编译工具链

如何建立Qt Creator交叉编译嵌入式Linux

如何在ubuntu下搭建qt交叉编译环境

在树莓派的交叉编译 qt 中构建应用程序时出错

如何构建MIPS交叉编译工具链