同样是 Ubuntu 20.04,为什么 Qt 程序放到嵌入式 Linux 目标板上却跑不起来?从 x86_64 到 aarch64 的跨平台编译与离线部署实战
很多人第一次做嵌入式或跨平台部署时,都会有一个很自然的判断:
既然开发机和目标板都是 Ubuntu 20.04,那把可执行程序直接拷过去运行,不应该有问题吗?
实际情况往往不是这样。
以一个典型场景为例:Qt 程序最早在 VMware 里的 Ubuntu 20.04 环境完成开发和编译,虚拟机架构是 x86_64;后面需要部署到一块嵌入式 Linux 目标板,而目标设备的 CPU 架构是 aarch64。
结果非常典型:
- 直接把 VMware 里编译出的程序拷到目标板上,报
Exec format error; - 改成
aarch64交叉编译后,程序能识别了,但又报缺少 Qt 运行库。
这篇文章把这两个问题一起讲清楚:为什么“同样是 Ubuntu 20.04”仍然跑不起来,以及正确的交叉编译和离线部署流程应该怎么做。
一句话先说结论
Linux 程序能不能运行,至少同时取决于三件事:
- 操作系统类型
- CPU 架构
- 运行时动态库依赖
所以:
Ubuntu 20.04 相同,不代表二进制程序就能直接通用。
一、先看两个最常见的报错
1. Exec format error
把 VMware Ubuntu 上编译出来的程序直接复制到目标板上运行,常见报错如下:
./your_qt_app: cannot execute binary file: Exec format error
或者中文环境下会看到:
二进制文件格式错误
这类错误的本质非常直接:目标板根本不认识这个程序的 CPU 架构。
用下面这条命令检查程序:
file ./your_qt_app
如果输出里包含类似信息:
ELF 64-bit LSB shared object, x86-64, ...
那就说明这个程序是 x86_64 版本,只能在普通 PC、笔记本、VMware 这类 x86_64 Linux 环境运行,不能直接放到 ARM64 目标设备的 aarch64 平台上。
2. 缺少 Qt 运行库
当我把程序改成交叉编译后的 aarch64 版本,再放到目标板上运行时,又遇到了第二类错误:
./your_qt_app: error while loading shared libraries: libQt5Core.so.5: cannot open shared object file: No such file or directory
这个错误和前面的 Exec format error 完全不是一回事。
它说明:
- 程序架构已经对了,目标设备已经能识别这个可执行文件;
- 但目标系统里缺少运行所需的 Qt 动态库;
- 问题已经从“架构不匹配”转成了“运行时依赖缺失”。
这一步其实是好现象,因为它意味着交叉编译方向已经走对了。
二、为什么同样是 Ubuntu 20.04 还是不能直接运行
很多人容易把“系统版本”和“二进制兼容性”混在一起。
Ubuntu 20.04 只是发行版版本,真正决定程序能不能跑起来的,是 CPU 架构和依赖环境。
常见概念可以这样理解:
x86_64 普通 PC、笔记本、VMware 虚拟机常见架构
amd64 Debian/Ubuntu 包管理中对 x86_64 的常见命名
aarch64 64 位 ARM 架构,嵌入式 Linux 设备中常见
arm64 Debian/Ubuntu 包管理中对 aarch64 的常见命名
所以即使两边都是 Ubuntu 20.04,也仍然可能是这样:
开发环境:Ubuntu 20.04 + x86_64
目标环境:Ubuntu 20.04 + aarch64
这两者的二进制并不兼容。
换句话说:
系统版本相同,只说明“软件生态可能接近”;架构相同,才意味着“二进制有机会直接运行”。
三、Qt 程序为什么还会报缺少动态库
如果你的 Qt 程序采用的是动态链接方式编译,那么即使 CPU 架构已经匹配,程序运行时仍然要依赖目标系统提供对应的共享库。
比如这类常见依赖:
libQt5Core.so.5
libQt5Gui.so.5
libQt5Widgets.so.5
libstdc++.so.6
libgcc_s.so.1
所以目标板要能运行程序,至少要满足两件事:
- 可执行文件本身是目标架构,比如
aarch64 - 系统里安装了同架构的运行库,比如
arm64的 Qt 库
这也是为什么你解决完架构问题以后,下一步往往会看到“缺少某个 .so”。
四、最有用的判断命令
在排查这种问题时,我最常用的其实就四类命令。
1. 看当前机器是什么架构
uname -m
常见输出:
x86_64
说明当前是普通 PC 或 VMware 的 x86_64 环境。
aarch64
说明当前是 64 位 ARM 平台,常见于很多嵌入式 Linux 目标设备。
2. 看程序本身是什么架构
file ./your_qt_app
如果结果里出现:
x86-64
说明它是 x86_64 程序。
如果结果里出现:
ARM aarch64
说明它是 aarch64 程序,方向就对了。
3. 看程序缺少哪些动态库
ldd ./your_qt_app
更实用的写法是直接过滤缺失项:
ldd ./your_qt_app | grep "not found"
如果没有任何输出,说明当前系统已经能找到所需动态库。
4. 用结果区分问题类型
这一步非常重要:
- 如果
file显示是x86-64,那是架构问题; - 如果
file显示是ARM aarch64,但ldd里有not found,那是运行库问题; - 如果
file和ldd都正常,程序还异常退出,就该继续排查业务逻辑或运行参数了。
五、正确的编译思路
1. 在 VMware Ubuntu 上编译本机版本
如果只是让程序在 VMware Ubuntu 或其他 x86_64 Linux 上运行,那么本机编译即可:
cd /path/to/your-project
chmod +x build-native.sh
./build-native.sh --install-deps
./build-native.sh --clean
生成文件通常在:
build/your_qt_app
验证一下:
file build/your_qt_app
如果显示 x86-64,这是正常结果,因为它本来就是给当前开发机运行的。
2. 为 ARM64 目标板编译 aarch64 版本
如果目标是 ARM64 架构的嵌入式 Linux 设备,就必须输出 aarch64 版本:
cd /path/to/your-project
chmod +x build-arm64.sh
./build-arm64.sh --install-deps --clean
后续重复编译可以简化成:
./build-arm64.sh --clean
生成文件通常在:
build-aarch64/your_qt_app
然后一定要验证:
file build-aarch64/your_qt_app
理想结果应包含:
ARM aarch64
这一步不要省,因为很多“明明我已经交叉编译了”的问题,最后发现输出文件其实还是本机架构。
六、把程序复制到目标板上运行
交叉编译完成后,把 ARM64 程序复制到目标板:
scp /path/to/your-project/build-aarch64/your_qt_app root@<target-board-ip>:/root/
然后在目标板上执行:
cd /root
chmod +x your_qt_app
./your_qt_app --help
如果这一步能显示帮助信息,说明程序本体已经具备运行条件。
如果还报缺少 .so,就继续用 ldd 查依赖,不要再回头怀疑“是不是交叉编译没成功”。
七、目标板不能联网时,怎么做离线依赖安装
这也是实战里很常见的一类问题。
如果目标板不能联网,那么这条命令就没法直接执行:
apt install -y <对应的Qt运行库包>
这时更稳的做法是:在能联网的 Ubuntu 开发机上,提前把 arm64 依赖包下载好,再复制到目标板离线安装。
1. 在 Ubuntu 开发机下载 ARM64 依赖包
前提是你的开发机已经配置好多架构源,可以下载 arm64 包。
mkdir -p ~/arm64_qt_debs
cd ~/arm64_qt_debs
apt download \
libqt5core5a:arm64 \
libqt5gui5:arm64 \
libqt5widgets5:arm64 \
libglib2.0-0:arm64 \
zlib1g:arm64 \
libstdc++6:arm64 \
libgcc-s1:arm64
再把程序本体也放进去:
cp /path/to/your-project/build-aarch64/your_qt_app .
然后打包:
tar czf arm64_qt_app_offline_pkg.tar.gz *
2. 复制到目标板
如果目标板能通过网络接收文件:
scp ~/arm64_qt_debs/arm64_qt_app_offline_pkg.tar.gz root@<target-board-ip>:/root/
如果网络不方便,也可以用 U 盘、共享目录或者其他离线介质传输。
3. 在目标板离线安装
mkdir -p /root/qt_app_pkg
cd /root/qt_app_pkg
tar xzf /root/arm64_qt_app_offline_pkg.tar.gz
dpkg -i *.deb
如果第一次安装提示依赖顺序问题,可以再执行一次:
dpkg -i *.deb
然后重新验证:
chmod +x your_qt_app
./your_qt_app --help
ldd ./your_qt_app | grep "not found"
如果还有缺失项,就继续补对应的 :arm64 包。
八、安装完成后,离线包还能不能删
可以删。
.deb 包和 .tar.gz 压缩包只是安装介质,安装完成后真正的动态库已经进入系统目录。确认程序可以运行后,完全可以把安装包清掉。
建议先做两步确认:
ldd ./your_qt_app | grep "not found"
./your_qt_app --help
如果没有 not found,并且程序能正常启动或输出帮助信息,就可以删除安装包:
rm -f *.deb
rm -f arm64_qt_app_offline_pkg.tar.gz
但要注意,不要删掉程序本体,也不要误删系统已经安装好的运行库。
九、最后给一个部署检查清单
如果你想快速判断问题出在哪,可以按下面这个顺序:
- 在目标板执行
uname -m,确认板子实际架构 - 对程序执行
file ./your_qt_app,确认是不是ARM aarch64 - 执行
ldd ./your_qt_app | grep "not found",确认依赖是否齐全 - 运行
./your_qt_app --help,确认程序能否真正启动
这四步足够覆盖大多数跨平台部署问题。
十、结论
这次踩坑最核心的一点,不是 Qt 写错了,也不是 Ubuntu 版本有问题,而是部署平台从 x86_64 切到了 aarch64。
一句话总结就是:
同样是 Ubuntu 20.04,不代表可执行程序可以直接通用;架构对了以后,还要把运行库补齐。
对应到这次项目里:
- VMware 本机编译出来的
build/your_qt_app只能给x86_64Linux 用; - ARM64 目标板应该使用交叉编译产物
build-aarch64/your_qt_app; - 如果目标板不能联网,就提前准备好
arm64的.deb依赖包离线安装。
更多推荐

所有评论(0)