☰
RK3568嵌入式Linux Qt交叉编译环境搭建与远程调试实战
2026/10/5 15:01:12 网站建设 项目流程

刚接触嵌入式Linux开发的同学,十有八九会在“交叉编译”这道坎上卡上一阵子。我自己当年第一次给瑞芯微RK3568开发板搭Qt开发环境时,就是因为在Ubuntu上直接编译了一个Qt程序,scp到板子上之后跑不起来,报了一堆乱七八糟的错误,折腾了两天才把环境彻底捋顺。现在回头看,核心问题其实只有一个:没有把宿主机(PC上的Ubuntu)和开发板之间的编译架构、运行库和工具链理顺。这篇文章就以RK3568开发板为例,完整走一遍在Ubuntu环境下搭建Qt交叉编译环境、配置Qt Creator、远程调试的流程,适合手里有一块RK3568开发板、想在板子上跑Qt界面程序,却不知道怎么把开发环境搭起来的同学。

1. 为什么必须交叉编译:一个崩溃程序的“破案”过程

1.1 RK3568是ARM架构,Ubuntu是x86架构,两者的“语言”不通

RK3568是瑞芯微推出的一款四核Cortex-A55处理器,是典型的ARM架构SoC。而我们的开发主机,无论是笔记本还是台式机,基本上都是x86_64架构。这里说的“架构”可以理解成机器各自使用的指令集:x86_64的机器能直接执行x86_64的二进制指令,ARM的机器则只能执行ARM指令。两个架构之间,CPU不认识对方的“母语”。

所以,当你把Ubuntu上编译出来的程序直接拷贝到RK3568开发板上,系统根本无法把它加载进来。最常见的执行结果只有两种:终端提示cannot execute binary file: Exec format error,或者所有依赖库都找不到、报No such file or directory。前者说明CPU架构对不上,后者常见的坑是我下面会专门展开的——程序是ARM二进制不假,但动态链接器路径不对,本质上是交叉编译时链接配置出错。

1.2 交叉编译的核心:在一台机器上编译出另一台机器能跑的程序

交叉编译(cross compile)的意思很简单:编译行为发生在宿主机(x86_64的Ubuntu)上,但编译产物的目标平台是开发板(aarch64架构)。整个链条里最核心的工具是交叉编译器,Ubuntu仓库里直接有对应的包:gcc-aarch64-linux-gnu和g++-aarch64-linux-gnu。装上之后,用aarch64-linux-gnu-gcc代替平时的gcc,编译出来的就是ARM机器能识别的二进制。

但仅仅有编译器还不够。一个Qt程序会依赖Qt运行库、C/C++标准库、动态链接器等一大堆支撑文件。这些“支撑文件”也必须是aarch64架构的版本。缺了它们,就算编译器生成了正确的ARM指令,程序在板子上依然起不来。这就是为什么交叉编译一个Qt项目,通常要先在Ubuntu上交叉编译出一整套aarch64的Qt库,再让Qt Creator指向这套库来写代码、编译程序。

1.3 为什么不直接在开发板上编译?

有同学会问:RK3568好歹是四核A55,为什么不能在板子上直接装GCC编译Qt程序呢?答案很简单:太慢,且不适合日常开发。板子的性能定位是嵌入式、工控、边缘计算,而不是作为一台编译服务器。我有一次尝试在RK3568板子上直接编译一个中等规模的Qt Widgets项目,光是编译链接就花了近十分钟,而同样的工程在PC上交叉编译只需要十几秒。再加上Qt库本身源码量巨大,在板子上编译整个Qt几乎是不可接受的。开发阶段频繁改代码、频繁编译,还是把Ubuntu当作编译主机来得舒服,板子仅作为程序运行和调试的目标设备。

2. 动手前先摸底:Ubuntu环境、工具链和板子信息一个都不能少

2.1 宿主机Ubuntu版本与基础依赖

我目前使用的宿主机系统是Ubuntu 20.04 LTS和22.04 LTS,两个版本都算稳定。如果你是其他较新的LTS版本,也完全可以,但务必保证软件源能正常更新。首先执行基础的依赖安装:

sudo apt update sudo apt install build-essential git wget bzip2 flex bison \ libx11-dev libxext-dev libxcb1-dev libfontconfig1-dev libfreetype6-dev \ libxkbcommon-dev libgl1-mesa-dev

这些依赖中有一部分是交叉编译Qt时configure阶段用来检测宿主环境的,有一部分是编译宿主机辅助工具(如qmake、moc、uic等)时需要的。很多教程只让你装build-essential,最后configure时会报“No XCB”之类的错,就是因为缺少这些X11相关头文件。这里一次装齐,可以省掉后面的连锁麻烦。

2.2 安装aarch64交叉编译工具链

Ubuntu仓库自带了aarch64交叉编译工具链,安装非常方便:

sudo apt install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu

安装完成后验证一下:

aarch64-linux-gnu-gcc --version

如果终端能正常输出版本信息,说明工具链已经可用。

这里补充一个选型建议:如果你的开发板是跟着厂商SDK来的,比如瑞芯微官方SDK、正点原子或迅为的BSP包里通常带了一份预编译的交叉编译器,位置一般在SDK目录的prebuilt/gcc/linux-x86/aarch64/下面。这种情况下,我建议优先使用厂商自带的工具链,而不是系统包的工具链,因为板子上的内核模块、二进制库很可能是用这份工具链编译的,大家用的GCC版本和C库版本一致,ABI兼容性更好。我的演示沿用apt安装的aarch64-linux-gnu-系列命令,原理完全一样。

2.3 摸清开发板底细:架构、系统与已有Qt

拿到一台开发板,不要急着写代码。先通过串口或SSH登进去,执行下面几条命令,把板子的“基因”摸清楚:

uname -a cat /etc/os-release ldd --version

我手上这台RK3568跑的是Debian系统,uname -a输出里有明确的aarch64字样,/etc/os-release能看出是哪个发行版,ldd --version则用于确认板子上的glibc版本。为什么要确认这些?因为后面我们在Ubuntu上交叉编译的程序,最终要动态链接到板子上的C库。如果Ubuntu交叉工具链对应的glibc版本比板子上的高,程序拷过去很可能报“version GLIBC_2.34 not found”。这是一个非常经典的坑,我在第6章会专门说排查方法。

接着看板子上是否已经带Qt运行库:

ls /usr/lib/aarch64-linux-gnu/ | grep libQt5

很多官方发布的根文件系统里其实只预装Qt运行库(为了跑预置程序),并不带头文件和开发用的.so链接文件。这意味着,即便板子上能跑Qt程序,想在Ubuntu上写代码链接到这套库也做不到。这也是我们为什么必须自己交叉编译一份完整Qt库的根本原因。如果你在板子上看到了libQt5Core.so等文件,记下它的版本号,尽量选择源码里对应的Qt版本交叉编译,保持两边一致。

3. 编译Qt库:给RK3568准备一整套“合身”的Qt

3.1 获取Qt源码与版本选择

交叉编译Qt,最主流的路线是下载Qt Open Source源码包,在Ubuntu上用交叉编译工具链构建出一套aarch64的Qt SDK。版本选择上,嵌入式领域用Qt 5的仍然非常多,因为对老模块支持好、配置成熟、资料也多。我以Qt 5.15.2为例,它在RK3568这类ARMv8平台上非常稳,也是我做项目的主力版本。Qt 6虽然在桌面端很红火,但在嵌入式板卡上的生态支持还处于过渡期,如果不追求新特性,稳定优先选5.15.2即可。

源码下载:

wget https://download.qt.io/archive/qt/5.15/5.15.2/single/qt-everywhere-opensource-src-5.15.2.tar.xz tar xf qt-everywhere-opensource-src-5.15.2.tar.xz cd qt-everywhere-opensource-src-5.15.2

3.2 configure参数:核心是 -xplatform、-prefix 和模块裁剪

Qt的交叉编译核心在于configure阶段。我的经历是,这个阶段90%的错误都出在参数没给对。下面给出一个我在实际项目中验证过的配置:

./configure -prefix /opt/rk3568/qt5.15.2 \ -opensource -confirm-license -release \ -xplatform linux-aarch64-gnu-g++ \ -no-opengl -no-glib \ -skip qtwebengine -skip qtwebview -skip qtwayland -skip qt3d \ -skip qtconnectivity -skip qtlocation -skip qtscript \ -no-feature-cups -no-iconv \ -make libs -nomake examples -nomake tests

几个关键参数我解释一下:

  • -prefix /opt/rk3568/qt5.15.2:交叉编译产物的安装目录。这个目录决定后面Qt Creator要选的qmake路径,也决定你要把哪些文件同步到开发板上。
  • -xplatform linux-aarch64-gnu-g++:告诉configure,目标是aarch64架构。Qt 5.15的源码里自带linux-aarch64-gnu-g++这个mkspec,如果你的Qt版本较老找不到,可以拷贝linux-arm-gnueabi-g++改成对应的aarch64版本。
  • -no-opengl:RK3568本身有GPU,但OpenGL在纯Buildroot/Debian环境下配置起来比较复杂,如果没有具体显示需求,先用软件渲染的linuxfb方式跑Qt界面最省事。后续需要GPU加速再重新编译带EGL支持的Qt版本也不迟。
  • -skip qtwebengine:Qt WebEngine编译极慢、体积巨大、依赖又多,嵌入式场景除非做浏览器,否则一定要跳过。类似的webview、wayland、3d模块也一并剪掉。
  • -no-iconv、-no-feature-cups:是为了避免configure阶段探测到宿主机/板子的iconv和CUPS环境不同而报错。实际项目中基本用不到这两个功能,关掉最稳妥。

3.3 make与make install:等待、验证与常见报错

configure跑完后会生成Makefile,接着编译:

make -j$(nproc) sudo make install

首次编译Qt库耗时较长,我测试过在8核CPU的机器上大约需要20到40分钟,取决于机器性能。如果中途出现编译错误,不要盲目重来,先看错误日志。最常见的有两类:一类是configure阶段没报错、make阶段报错,基本是缺少宿主工具或依赖头文件,回头补装对应依赖后需要重新configure;另一类是工具链版本过老,比如Qt 6要求GCC 10以上,但系统包默认给的gcc-aarch64-linux-gnu版本是7或8,这时候就需要升级工具链,或者改用厂商或Linaro的较新版本。

编译完成后验证:

file /opt/rk3568/qt5.15.2/lib/libQt5Core.so

输出里应该包含ELF 64-bit LSB shared object, ARM aarch64字样,说明这套Qt库确实是aarch64架构的,可以放心提供给后续Qt Creator使用。

4. Qt Creator中的Kit配置:把编译器、Qt和设备串成一个完整链条

4.1 Qt Creator版本选择与基本准备

Qt Creator本身是一个IDE,需要从Qt官网单独下载安装,或者通过Qt在线安装器安装。它最大的价值在于把编译器、Qt库、设备、调试器这些松散组件关联成一套可用的工具链。很多人只会在Qt Creator里新建工程、点运行,却从没理解过底层的Kit逻辑,导致交叉编译时各种“跑不起来”。

安装完成后,先别急着建项目,我们把环境一项项配好。打开Qt Creator,选择菜单栏的“工具”->“选项”,进入后能看到设备、编译器、Qt版本、Debuggers等配置页。

4.2 添加编译器与Qt版本

这一步的作用是告诉Qt Creator:我们有一套专门面向aarch64的GCC编译器,还有一套交叉编译出来的Qt库。在“Kits”->“编译器”页面,点“添加”->“GCC”->“C”,编译器路径选择:

/usr/bin/aarch64-linux-gnu-gcc

同样,再添加一个“C++”编译器:

/usr/bin/aarch64-linux-gnu-g++

添加完后,Qt Creator会自动识别出它们是aarch64架构。如果识别不准确,可以手动在编译器页面把ABI设置为aarch64-linux-generic-elf-64之类的ARM64选项。

接着在“Kits”->“Qt版本”页面,点“添加”,路径选择:

/opt/rk3568/qt5.15.2/bin/qmake

看到版本信息显示为Qt 5.15.2,说明这套Qt库已被Qt Creator识别。

4.3 添加设备:让Qt Creator认识你的RK3568

设备配置是为了后续的远程部署、远程运行和远程调试。在“设备”页面,点“添加”->“泛型Linux设备(Generic Linux Device)”,填写开发板的IP地址、用户名和密码。我的板子系统账号是root,就填root和对应的密码或密钥。填完后点“测试”,确保Qt Creator能通过SSH连上板子并执行基本命令。如果测试失败,先确认板子SSH服务是否开启,以及Ubuntu和板子之间的网络是否互通。

4.4 新建Kit并验证编译产物架构

在“Kits”页,点“添加”,给这个套件起一个清晰的名字,比如“RK3568-qt5.15.2”。然后分别做以下关联:

配置项选择内容
编译器Caarch64-linux-gnu-gcc
编译器C++aarch64-linux-gnu-g++
调试器aarch64-linux-gnu-gdb(没有就先用系统gdb-multiarch)
Qt版本Qt 5.15.2(/opt/rk3568/qt5.15.2)
CMake工具宿主机的cmake(如果你用CMake构建)
设备RK3568开发板
Sysroot可先留空,后续有需要再指定板子的根文件系统

这里我要单独说一下Sysroot。它本质上是一个“.sdk目录”,把开发板的根文件系统拷贝一份到Ubuntu上,里面包含了板子上的/usr、/lib目录。设置Sysroot后,交叉编译时编译器会优先从这个目录里找头文件和库,从而保证编译链接使用的版本与板子实际运行环境完全一致。对于精度要求很高的项目,Sysroot是必需品;如果图省事,很多情况下不设也能跑,但遇到链接到板子独有库的项目,就可能出现“找不到头文件”的诡异问题。

选好Kit后,新建或打开一个最简单的Qt Widgets项目,构建套件选择“RK3568-qt5.15.2”,然后点击“构建”。编译完成后,找到生成的二进制文件(一般在build目录下),用file命令验证:

file ./build-TestApp-.../TestApp

如果输出是ELF 64-bit LSB executable, ARM aarch64,说明你的Qt Creator交叉编译搭建已经成功。这一步是最重要的分水岭:能生成aarch64可执行文件,说明编译链路通了;后面要做的只是部署和调试。

5. 部署到RK3568并远程调试:程序和开发板正式“接上头”

5.1 配置部署步骤:把可执行文件送到板子上

程序编译出来并不等于能在板子上跑,还需要把可执行文件和它依赖的Qt库一起部署到开发板。部署最直接的方式是手动scp,但对于频繁开发调试的场景,手动操作太低效。Qt Creator提供了一键部署功能,非常值得配置。

打开项目窗口左侧的“项目”标签页,在你的Kit配置里找到“部署”步骤,点“添加部署步骤”。Qt Creator支持通过“SFTP上传文件”这个方式,把本地的可执行文件上传到开发板的指定目录,比如/opt/app/。配置好之后,每次点绿色三角运行按钮,Qt Creator会先自动编译,再通过SSH把可执行文件复制到板子上,最后在板子上启动程序。

部署时要注意:Qt程序是动态链接的,二进制本身只有几百KB甚至几MB,但运行依赖的libQt5Core.so、libQt5Widgets.so、libQt5Gui.so等一堆动态库加起来可能有大几十MB。编译好的Qt库在Ubuntu的/opt/rk3568/qt5.15.2/lib目录下,你需要把整个lib目录拷贝到板子上:

scp -r /opt/rk3568/qt5.15.2 root@<板子IP>:/opt/

然后在板子上设置环境变量:

export LD_LIBRARY_PATH=/opt/qt5.15.2/lib:$LD_LIBRARY_PATH export QT_QPA_PLATFORM_PLUGIN_PATH=/opt/qt5.15.2/plugins

LD_LIBRARY_PATH让动态链接器优先从新目录寻找Qt库,QT_QPA_PLATFORM_PLUGIN_PATH告诉Qt去哪里找平台插件。这两行不设,后面十有八九会碰到Could not load the Qt platform plugin "xcb"或者libQt5Core.so.5找不到的问题。

5.2 远程运行与远程调试:Qt Creator如何接管板子上的进程

远程运行配置在“项目”->“运行”页面。构建配置里选择“作为远程运行”,远程可执行文件路径写板子上的部署目录,比如/opt/app/TestApp,工作目录也一并设置。参数部分根据程序需要,如果要用linuxfb平台显示,就加-platform linuxfb。

远程调试是这篇文章的重头戏,也是很多人觉得玄乎的地方。简单说,远程调试的原理是:板子上运行一个gdbserver程序,宿主机上的GDB通过网络与它通信,控制板子上的被调试程序。Qt Creator通过SSH在板子上自动启动gdbserver,再把宿主机上交叉编译出的调试信息与板子上的程序符号对应起来,于是你就能像调试本地程序一样,在Ubuntu的Qt Creator界面里看到板子上程序的断点、变量、调用栈。

要使远程调试跑通,三个东西必须齐活:

  1. 板子上有gdbserver。通过板子自带的包管理器安装即可,Debian/Ubuntu系:

    apt install gdbserver

    如果板子系统是裁剪过的Buildroot,可能需要自己在Buildroot里开启gdbserver并重新烧录根文件系统。

  2. 宿主机有一个支持aarch64的GDB。优先安装gdb-multiarch:

    sudo apt install gdb-multiarch

    然后在Qt Creator的“Kits”->“调试器”里,为“RK3568-qt5.15.2”这个套件手动指定调试器路径为/usr/bin/gdb-multiarch。

  3. 被调试的程序保留调试符号,不要用strip处理。平时编译release版本时Qt Creator可能会自动strip,要在项目构建步骤里把“Strip二进制文件”选项关掉,或者干脆用debug模式编译远程调试版。

都准备好后,直接在Qt Creator里点调试按钮(F5),它会自动完成部署到板子、启动gdbserver、建立连接这一连串动作。第一次跑通远程调试时,你会在Qt Creator的Debugger Console里看到类似Remote debugging from host 192.168.1.100的日志,这就说明板子上的gdbserver已经挂上了。

5.3 让程序在板子上正常显示:Qt平台插件选型

Qt程序在嵌入式设备上显示,与桌面Linux环境下“直接弹窗”不一样,它依赖QPA(Qt Platform Abstraction)平台插件。最常用的三种:

平台插件适用场景说明
linuxfb无窗口系统、有framebuffer最简单,裸机或Buildroot环境首选
eglfs有GPU/EGL环境需要Mali等GPU驱动支持,性能好
xcb板子系统带X11显示服务如果板子跑的是完整桌面版Debian/Ubuntu才使用

我交叉编译Qt时用了-no-opengl,因此可用的平台插件主要是linuxfb。在板子上执行:

export QT_QPA_PLATFORM=linuxfb ./TestApp

如果程序启动后没有任何错误输出,而屏幕上出现了Qt界面,说明显示链路已经通。这里有一个常见误区:很多人在xcb平台上卡了很久,实际上板子根本没有X11服务,怎么等都等不出窗口。搞清楚自己板子的显示架构,再选择对应的平台插件,可以少走很多弯路。

6. 这些坑我踩过:编译、链接与运行阶段的典型问题排查

6.1 “cannot execute binary file”其实是架构不匹配

这是最基础的报错。看到cannot execute binary file,第一反应是检查二进制架构:

file ./TestApp

如果输出显示的是x86-64,说明你编译时选错了Kit,Qt Creator里构建套件没有切到“RK3568-qt5.15.2”,而是用了默认的桌面Qt。这种情况不用动代码,重新选择构建套件编译即可。如果输出已经是ARM aarch64,但依旧无法执行,那就不是架构问题,而是动态链接器或库路径的问题。

6.2 动态链接库失踪案:ldd与readelf的用法

程序能加载但报No such file or directory,或者运行时报找不到libQt5Core.so.5,这属于动态链接问题。在开发板上用下面的命令查看程序依赖了哪些动态库:

ldd ./TestApp

如果发现libQt5Core.so显示not found,说明LD_LIBRARY_PATH没设置对,或者依赖的Qt库版本与程序编译时不一致。另一种情况是二进制里的动态链接器路径不对,用readelf确认:

readelf -l ./TestApp | grep interp

正常情况下应该显示/lib/ld-linux-aarch64.so.1。如果你的交叉编译工具链C库路径与板子不一致,这里可能指向一个不存在的位置,结果就是“No such file or directory”。遇到这种问题,可以在开发板上建立对应的软链接,或者重新用与板子glibc版本匹配的工具链编译。

6.3 gdb远程调试时的两个高频问题

远程调试最让人抓狂的报错是Remote 'g' packet reply is too long。这个错误几乎都是宿主机GDB与板子gdbserver版本不匹配造成的,比如板子上的gdbserver是GDB 10,宿主机上的gdb是GDB 8。解决办法很直观:升级宿主机上的gdb-multiarch到最新版,确保两边的GDB协议版本兼容。

另一个常见问题是断点打不上。程序明明跑起来了,但Qt Creator里怎么点断点都无效,原因多半是二进制被strip过,调试符号丢失。只需关闭strip选项,或者用debug模式重新编译即可。还有一个容易忽略的点:如果程序已经部署到板子上,而宿主机GDB加载的符号文件路径与实际部署路径不一致,也可能导致源码无法关联。Qt Creator里可以通过“调试”->“调试模式”->“源码文件映射”手动纠正宿主源码路径与板子源码路径的对应关系。

6.4 一点实用的调试习惯

最后分享一个我自己的习惯:远程调试之前,永远先在板子上手动跑一遍程序。

gdbserver :2345 ./TestApp

gdbserver启动成功后,宿主机上再用命令行GDB连上:

gdb-multiarch ./TestApp (gdb) target remote <板子IP>:2345 (gdb) continue

先在命令行方式下跑通一次远程调试,再回到Qt Creator图形界面操作,能避免很多环境类的低级问题。因为当程序无法启动、插件加载失败、环境变量缺失这些基础问题没有解决时,图形化调试只会让错误变得更难定位。先命令行跑通,再图形化调试,这是我整个RK3568开发过程中觉得最省时间的做事顺序。

RK3568的Qt交叉编译环境搭建,说穿了就是“架构匹配”“工具链一致”“路径正确”这三件事。把每一步验证到位,剩下的就是正常的Qt开发流程了。等你把第一个Qt程序成功部署到板子上、在屏幕上看到自己的界面时,那种感觉,值得前面所有折腾。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询