ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

嵌入式Linux下的ARM交叉编译:工具链选型、Qt构建与QEMU验证

嵌入式Linux下的ARM交叉编译:工具链选型、Qt构建与QEMU验证 1. 为什么嵌入式开发绕不开交叉编译先从一个很现实的场景说起。你手里拿到一块飞腾或RK3576的开发板想在上面跑一个自己写的C程序或者编译一份Qt库给ARM环境用。如果你像我一样最开始习惯性地在x86的笔记本上执行gcc -o hello hello.c然后把二进制文件拷贝到板子上大概率会看到一个刺眼的报错cannot execute binary file: Exec format error。这时候才意识到x86和ARM是两个世界。核心理由很简单x86用的是CISC复杂指令集ARM用的是RISC精简指令集两者机器码完全不兼容。你在笔记本上编译出来的二进制CPU看不懂自然跑不起来。那是不是把开发环境整个搬到ARM板子上直接在板子上编译可以但代价很高。嵌入式板子的CPU性能通常只有桌面级的几分之一甚至几十分之一编译一个大型项目像Qt、Linux内核可能要等几个小时甚至过夜而且板子的存储空间、内存都有限装不下完整的工具链。更麻烦的是很多项目的构建依赖网络下载第三方库开发板上的网络环境往往不如PC方便。所以行业里几乎统一的做法就是交叉编译在性能强劲的x86宿主机上安装一套目标平台ARM的编译工具链编译出ARM架构的机器码再拷贝到板子上运行。这个流程看似朴素但背后牵扯到工具链选型、sysroot配置、依赖库处理、链接器行为等一系列细节任何一个环节出错都会让你在编译不过运行崩溃找不到库三个坑里反复横跳。这个DAY17的笔记我就把ARM架构和交叉编译这条线从头到尾捋一遍从原理到实操到排错覆盖ARM的基础认知、工具链怎么选、Qt 5.12.10交叉编译的完整流程以及基于QEMU的验证手段。适合两类人看一是刚接触嵌入式Linux、准备做第一个交叉编译项目的同学二是已经在做嵌入式开发、但每次配置交叉编译环境都要查半天资料的老手。2. ARM不是一种芯片而是一整套体系2.1 指令集架构、微架构与SoC的三层关系很多初学者把ARM架构理解成一种芯片型号这是第一个误区。ARM公司本身不生产芯片它只做两件事设计指令集架构ISA以及基于这套指令集设计处理器核比如Cortex-A72然后以IP授权的形式卖给芯片厂商。芯片厂商高通、海思、飞腾、瑞芯微等买下这些IP再集成GPU、NPU、内存控制器、各种外设控制器封装成一颗完整的SoC片上系统。这三层的关系可以用搭积木来理解指令集架构是游戏规则规定了CPU能做什么操作比如ADD、LDR、STR这些指令长什么样微架构是游戏引擎的实现同样的规则可以有不同的实现方式有的主频高、有的省电SoC则是整台游戏机除了CPU还带显卡、声卡、网卡这些配件。理解了这层关系你就明白为什么很多交叉编译教程反复强调编译器参数要和目标CPU匹配。因为同一个ARM指令集架构下还有细分ARMv7、ARMv8而ARMv8又分AArch6464位和AArch3232位。如果你的工具链是32位ARM的编出来的程序在64位内核上可能能跑内核一般兼容但在纯64位用户态环境下会出问题反过来64位程序在32位内核上铁定跑不了。所以交叉编译第一步先确认目标平台到底是哪种uname -m输出aarch64表示64位ARMv8输出armv7l表示32位ARMv7。2.2 ARM和x86的真正差异在哪里抛开教科书上CISC vs RISC的高大上定义我从开发者视角说几个实际体会到的差异。第一是对齐访问。x86几乎允许你随意访问未对齐的内存地址顶多性能慢一点。但ARM在默认配置下访问未对齐地址会直接抛异常。比如你把一个int类型指针指向了一个奇数地址在x86上运行正常在ARM上一运行就是SIGBUS崩溃。这种问题在你做协议解析、字节流拆包的时候特别容易遇到因为改动一个结构体定义内存布局就变了。第二是字节序。ARM处理器支持大小端两种模式但Linux生态几乎都是小端。跨架构传输数据时如果发送方和接收方字节序不一致解析出来的数值就会完全错乱。实际项目中网络协议统一用大端传输本地数据统一用小端这是通用做法但如果你的嵌入式设备在本地文件里存了大端数据又随手在x86上做解析坑就来了。第三是指令集槽位和立即数。ARM指令是固定32位长度Thumb模式是16位指令里能携带的立即数位数有限。这就导致一个现象在x86上写一个mov eax, 0x12345678可能一条指令就搞定但ARM可能要拆成几条指令加载。所以你会发现ARM的汇编函数里加载一个大常量的代码比x86更加啰嗦。这种差异平时写高级语言感受不到但看反汇编、做性能优化的时候就能体会到。2.3 认识Cortex-A、Cortex-R、Cortex-MARM处理器的产品线大致分三条Cortex-A、Cortex-R、Cortex-M。Cortex-AApplication面向应用处理器跑Linux、Android这种复杂系统性能强支持MMU我们做嵌入式Linux交叉编译主要面对的就是它。市面上常见的RK3568、RK3576、飞腾系列都属于这类。Cortex-RReal-time面向实时性要求高的场景比如汽车电子、存储控制器它没有MMU但中断响应非常快一般跑RTOS或者裸机程序。Cortex-MMicrocontroller面向单片机比如STM32资源极其有限通常跑裸机或者RT-Thread、FreeRTOS这类微内核OS。这三种处理器对编译的要求完全不同。Cortex-M的裸机程序用arm-none-eabi-工具链不带Linux内核也没有动态链接器这种东西Cortex-A跑Linux的应用程序用arm-linux-gnueabihf-或者aarch64-linux-gnu-工具链带完整的C库和动态链接机制。很多人在STM32上用惯了arm-none-eabi-gcc转到嵌入式Linux上还想用同一套结果各种诡异报错。实际上这两个世界连编译器的目标三元组都不一样工具链几乎不通用。3. 交叉编译工具链的选型与自建踩坑记3.1 成品工具链怎么选三套主流方案对比工具链可以买现成的也可以自己用crosstool-NG构建还可以从芯片厂商定制。我整理了一个对比表格方便你快速定位自己该用哪套方案典型名称适用场景优点缺点Linaro GNU工具链aarch64-linux-gnu-gcc7.5/9.2等通用ARMv8平台最推荐更新及时社区使用广Bug修复快非芯片厂家定制部分SoC专有特性需手动设置芯片厂商SDK自带飞腾、瑞芯微RK3576 SDK里的工具链明确面向某款SoC时针对性强编译器版本和板子配套版本往往偏老升级不便发行版自带Ubuntu/Debian的gcc-aarch64-linux-gnu只做简单程序、验证流程安装方便一条命令搞定版本随发行版走不能定制GLIBC版本受限crosstool-NG自建自定义arm-cortex_a8-linux-gnueabihf需要老版本GLIBC、特定优化完全可控交叉编译内核和Bootloader的厂家都爱用构建过程漫长出错率高最简单的方式跑在ARM64 Linux板子上的用户态程序优先选Linaro的aarch64-linux-gnu-gcc版本无脑选最新的LTS即可比如gcc 9或gcc 11分支。如果板子Ubuntu系统自带aarch64-linux-gnu-gcc也可以直接用省得手动下载。注意一个细节同样叫aarch64工具链不同版本编译出来的二进制调用的GLIBC版本可能不一样。比如Ubuntu 20.04的交叉工具链默认GLIBC 2.31你编译完拷到板子上板子的GLIBC是2.28程序一启动就报version GLIBC_2.31 not found。这种问题在真实项目中最常见后面专门有章节说排查思路。3.2 自建工具链流程照着做一遍就记住如果买不到合适的成品工具链或者目标平台太冷门比如要用老旧的arm compiler 5.06给ARM11处理器做裸机开发那自建工具链就绕不开。我不推荐你从头编译GCC那是个大工程新手基本一遍成不了更建议用crosstool-NG半自动构建。大致流程是这样安装依赖。Ubuntu上执行sudo apt install autoconf automake bison flex gawk texinfo help2man libtool libtool-bin libncurses5-dev。注意libncurses5-dev在很多新发行版上没有用libncurses-dev替代就行。下载crosstool-NG源码解压后执行./configure --enable-local然后make把工具装到当前目录执行./ct-ng可以看到交互菜单。选择目标配置。你可以复制自带的样例./ct-ng aarch64-unknown-linux-gnu或者./ct-ng arm-cortex_a8-linux-gnueabihf。然后./ct-ng menuconfig配置项主要是glibc版本、GCC版本、Linux内核头文件版本。配置的关键在于版本匹配。GCC版本太新、GLIBC版本太老会编译失败内核头文件版本和GLIBC也有隐含要求。最稳妥的方式是选一个已知组合比如GCC 9.3.0 GLIBC 2.31 Linux 4.19头文件这个组合在真实项目里验证过非常多。执行./ct-ng build然后就可以去喝杯咖啡了。整个构建一般要30到60分钟取决于机器性能。构建完成后工具链输出在~/x-tools目录下里面的bin目录就是编译器的位置。挂到PATH环境变量里就能用。自建工具链的好处是你可以精确控制GLIBC、GCC、甚至CPU优化参数。比如你要给飞腾ARM平台做一个极其精简的发行版某些编译参数必须匹配飞腾的微架构——这类需求下厂商SDK或Linaro预编译版不一定能满足自建几乎是唯一路径。3.3 工具链前缀命名规则必须一眼看懂交叉编译工具链的可执行文件都带前缀比如arm-linux-gnueabihf-gcc、aarch64-linux-gnu-gcc我建议你把前缀拆成三段解读archarm是32位ARMaarch64是64位ARMos-abilinux表示目标系统是Linuxnone表示裸机bare-metal环境rtems等是其他RTOS浮点ABIeabi表示嵌入式ABIeabihf表示带硬浮点也就是浮点运算直接由FPU硬件完成不需要软件模拟。gnu这一节更像是一个发行版打标说明用的是GNU C库glibc而不是uClibc或musl。用错浮点ABI是一个非常隐蔽的坑。工具链带hf编译选项里也开了硬浮点但链接只链到了软浮点的库就会出现一堆undefined reference to __aeabi_dadd这种符号找不到的错误。反过来软浮点工具链在带FPU的Cortex-A系列上性能会差不少因为所有浮点运算都被编译器换成函数调用了。4. Qt 5.12.10交叉编译完整流程与依赖处理4.1 环境准备从零到能configure我拿目前网上搜索热度一直很高的qt5.12.10交叉编译做一个完整演示。这个版本虽然不是最新但胜在稳定很多老项目还在用。宿主环境假设Ubuntu 20.04 x86_64目标是ARM64aarch64开发板文件系统rootfs已经准备好了。Qt交叉编译最大的难点不是Qt本身而是依赖让Qt的configure脚本能正确找到目标板上的依赖库zlib、libpng、libjpeg、openssl、sqlite等同时不能误用宿主机上的x86库。先安装好交叉编译工具链sudo apt update sudo apt install -y build-essential gcc-aarch64-linux-gnu g-aarch64-linux-gnu然后准备Qt源码。下载qt-everywhere-opensource-src-5.12.10.tar.xz解压后进入源码根目录。Qt的构建不建议直接执行make install而是创建一个独立的构建目录mkdir -p ~/qt-build-aarch64 cd ~/qt-build-aarch64为什么要独立构建目录因为源码目录如果用不同的配置重复构建会残留前一次构建的config.cache和Makefile片段导致每次configure结果不一致。我见过太多人直接在源码目录里build结果一次改配置后怎么都编译不过最后只好重新解压。4.2 configure参数逐项讲解下面是常用的configure参数每条我都标注了为什么必须这样设../qt-everywhere-src-5.12.10/configure \ -prefix /usr/local/qt5.12.10-aarch64 \ -opensource -confirm-license \ -xplatform linux-aarch64-gnu-g \ -device linux-generic-g \ -nomake examples -nomake tests \ -no-opengl -no-xcb \ -no-gtk -no-cups \ -qt-libpng -qt-libjpeg -qt-zlib \ -sql-sqlite \ -no-use-gold-linker说明几个关键参数-xplatform linux-aarch64-gnu-g这个是告诉Qt构建系统你是在x86上编译aarch64版本对应qmake平台上的一组mkspec。如果没有这个Qt会尝试直接用宿主g编译一上来就报错。-device linux-generic-g指定目标设备类型。Qt官方有很多设备定义文件但在通用开发板上建议用linux-generic-g避免它启用一些特定设备的硬件加速特性导致编译失败。-no-opengl -no-xcb如果你的板子没有GPU或者你只需要跑一个无界面服务程序OpenGL和X11都关掉最省心。否则configure阶段会链接宿主机的xcb库产生的库文件拷贝到板子上会因为依赖宿主机库而运行失败。-qt-libpng -qt-libjpeg -qt-zlib这三个是让Qt自己编译这三个基础库而不是链接宿主机的版本。这是交叉编译最常见的坑如果写-system-zlibQt会认为zlib已经安装去链接/usr/lib/x86_64-linux-gnu/下的库链接器立刻报架构不兼容。使用Qt自带源码编译最省事。还有一个容易忽略的点configure之前要设置环境变量让工具链能找到sysroot里的库和头文件。export PATH/usr/bin/:$PATH export QT_SYSROOT/path/to/your/rootfs export PKG_CONFIG_PATH/path/to/your/rootfs/usr/lib/aarch64-linux-gnu/pkgconfig export PKG_CONFIG_LIBDIR/path/to/your/rootfs/usr/lib/pkgconfig如果不设QT_SYSROOTQt的交叉编译模块不知道去哪里找目标板上的头文件和库configure会扫描宿主机的/usr/include到时候编译出一堆带着x86依赖的Qt库。4.3 编译安装与搬运configure通过之后执行make -j$(nproc) sudo make install-j$(nproc)可以充分利用多核但也要适度。Qt编译很吃内存比如-j16同时编译16个编译单元时内存峰值可能超过8GB8GB内存的机器容易编译过程中被OOM杀掉。稳妥的是make -j4或者-j8。安装完成后交叉编译好的Qt库会放在你-prefix指定的目录。把它整个打包拷贝到开发板的/usr/local/qt5.12.10-aarch64目录下。然后在开发板上配置环境变量export LD_LIBRARY_PATH/usr/local/qt5.12.10-aarch64/lib:$LD_LIBRARY_PATH如果你想在板子上开发Qt程序还需要拷贝工具链里的aarch64-linux-gnu-gcc对应的连接器、strip等工具到板子上的交叉编译环境不过一般流程是在PC端编写Qt代码用qmake生成Makefile编译出ARM版可执行文件再拷贝到板子上跑。开发板只需要Qt的运行库不需要Qt的开发工具。4.4 交叉编译一个Qt程序示例假设你在PC上已经交叉编译好了一个简单的mainwindow程序交叉编译时也要用arm版qmake而不是宿主机qmake。在PC上执行export PATH/usr/local/qt5.12.10-aarch64/bin:$PATH cd ~/myapp ~/qt-build-aarch64/bin/qmake myapp.pro make这里容易踩坑如果你在开发板上没有装Qt开发环境qmake生成的Makefile里链接器、编译器路径全是PC上交叉工具链的绝对路径只能在PC端编译。所以整个开发闭环是这样的PC交叉编译 - 拷贝二进制和依赖的Qt库到板子 - 板子上运行依赖都在开发板的/usr/local/qt5.12.10-aarch64目录里。我用一个demo验证#include QApplication #include QLabel int main(int argc, char *argv[]) { QApplication app(argc, argv); QLabel label(Hello from ARM Qt); label.show(); return app.exec(); }在开发板上跑起来后如果能正常显示Hello from ARM Qt说明Qt库和程序架构匹配交叉编译环境基本搭建成功。如果报error while loading shared libraries: libQt5Widgets.so.5: cannot open shared object file: No such file or directory就是LD_LIBRARY_PATH没设置好或者Qt安装路径不一致。5. 验证之道QEMU模拟与真机部署5.1 没有开发板时怎么验证交叉编译产物很多人卡在代码写好了手头没开发板这一步。在买板子之前你可以用QEMU模拟ARM环境一边调一边学板子到了直接无缝衔接。以aarch64为例安装和启动都非常简单sudo apt install -y qemu-user-static qemu-system-arm然后可以用qemu-aarch64直接运行交叉编译的可执行文件不需要一个完整的系统镜像。我经常用这种方式快速测试一个C程序是否依赖了宿主机的库aarch64-linux-gnu-gcc -static -o hello_arm hello.c qemu-aarch64 ./hello_arm-static表示静态链接把所有依赖的glibc库都编进二进制里。这样用QEMU运行时不需要在模拟环境里安装额外的libc。不过现实项目中我们一般不用静态链接因为库之间相互依赖全静态会导致二进制非常庞大、磁盘和内存占用都增加。所以更常见的验证方式是准备一个完整的rootfs比如从网上下载一个大米版的Ubuntu-base-arm64解压用chroot加qemu-aarch64-static直接进去跑动态链接的二进制。sudo mount --bind /proc rootfs/proc sudo mount --bind /sys rootfs/sys sudo mount --bind /dev rootfs/dev sudo mount --bind /dev/pts rootfs/dev/pts sudo chroot rootfs /usr/bin/qemu-aarch64-static /bin/bash进入chroot后的/bin/bash就是ARM版的。你可以在里面执行所有ARM架构的原生程序包括动态链接的Qt程序。唯一注意qemu-aarch64-static要拷贝到rootfs的/usr/bin/目录下chroot里才能找到。这个方式在调试依赖较多的大型程序时非常实用比每次都拷贝到真机快得多。5.2 QEMU跑Linux内核与Buildroot简单程序用QEMU user模式验证就够了但如果要做内核开发、Bootloader调试就需要QEMU system模式。这时候一般工具链配套Buildroot或者Yocto来构建一个精简根文件系统再用QEMU启动。Buildroot的流程是make qemu_aarch64_virt_defconfig它会生成一个针对QEMU ARM虚拟机的配置然后make等它把交叉编译工具链、busybox、内核、根文件系统全打好。最后用生成的start-qemu.sh脚本直接启动。QEMU system模式跑内核对交叉编译验证的效果非常直观你能在宿主机上看到完整的内核启动日志可以挂载根文件系统看到ARM架构的行为方式。当年我调试一个飞腾平台的内核板子还没到货就是靠QEMU先把驱动模块的流程捋通的。5.3 真机部署前必做的三个检查真机部署之前强烈建议按下面的清单自我检查一遍file命令确认架构和链接方式。在宿主机上执行file myapp输出里应该有ARM aarch64字样且dynamically linked动态链接或statically linked静态链接。ldd在qemu-chroot环境下验证动态库依赖。如果依赖里出现libc.so.6 /lib/aarch64-linux-gnu/libc.so.6说明库路径正确如果出现not found说明sysroot配置有问题需要检查-Wl,-rpath或LD_LIBRARY_PATH。readelf -h查看程序头里的Machine类型。Machine: ARM aarch64表示架构正确Machine: Intel 80386表示编译错了。这三步只要有一项不过关拷到真机上要么不能执行要么启动崩溃。6. 高频报错与排查链路记录6.1GLIBC_2.31 not found类版本冲突这类报错最常见出现场景一般是用较新的交叉工具链编译完程序拷到老系统板子上运行。根因是程序动态链接时要求的目标板GLIBC版本高于目标板实际安装的版本。排查链路我从头到尾给你过一遍先用readelf -V myapp | grep GLIBC_查看程序实际引用的GLIBC版本集合。再到板子上执行strings /lib/aarch64-linux-gnu/libc.so.6 | grep GLIBC_查看板子实际提供的GLIBC版本。对比两个集合确定是哪个符号版本不满足。解决方案有三个一是换低版本的工具链重新编译二是把新版的libc.so.6和ld-linux-aarch64.so.1拷到板子完全替换系统GLIBC风险极高不推荐在量产设备上直接替换三是在PC上用老版本的sysroot对程序做链接也就是让编译器的sysroot里包含老版本的GLIBC。我推荐用Linaro工具链搭配对应GLIBC的sysroot。比如你做麒麟系统下的ARM应用官方SDK里就带了一套sysroot直接用它编译基本不会出现版本问题。前提是你别自己额外装一个宿主机的新工具链去覆盖SDK的环境变量。6.2cannot find -lxxx链接失败链接阶段报cannot find -lxxx比如cannot find -lQt5Core有几种可能头文件和库文件路径没加对。需要-I/path/to/arm/include -L/path/to/arm/lib且这个lib目录里确确实实是ARM版的库而不是x86的。拿file libQt5Core.so一看便知。库名不匹配。-lQt5Core会去找libQt5Core.so如果你的库名叫libQt5Core.so.5.12.10链接器默认找不到。这时候需要建立一个符号链接ln -s libQt5Core.so.5.12.10 libQt5Core.so。链接顺序问题。在GCC的链接命令行里被依赖的库要放在依赖它的目标文件之后。比如gcc main.o -lQt5Core -o myapp不能写成gcc -lQt5Core main.o -o myapp。我见过小白在Qt工程的.pro文件里乱改LIBS顺序编译时疯狂报undefined reference就是这个原因。6.3qemu: uncaught target signal 11 (Segmentation fault)运行崩溃QEMU user模式下经常遇到这个问题。第一种情况是程序里犯了对齐访问错误你抓个gdb挂上去查。QEMU user模式下可以传入-g 1234开一个远程调试端口qemu-aarch64 -g 1234 ./myapp然后在宿主机上aarch64-linux-gnu-gdb -ex target remote :1234 ./myapp这样就能看到崩溃时程序停在哪个函数、哪个指令。第二种情况是程序动态链接了不相容的库例如把x86的库误当成ARM库链接了进来或者Qt库版本不一致。这种情况下建议用-strace参数qemu-aarch64 -strace ./myapp 21 | head -50如果看到一堆openat(/lib/xxx.so, O_RDONLY|O_CLOEXEC) -1 ENOENT基本可以把问题锁定在库路径或库缺失上。6.4 交叉编译内核模块的Headers地狱最后一个高频坑是编译内核模块时宿主机上装了新版Linux头文件但目标板上跑的是另一个版本内核导致vermagic不匹配insmod时报Invalid module format。解决思路其实简单模块的编译依赖目标板内核的Module.symvers和build目录不是宿主机头文件。你在板子上找到/lib/modules/$(uname -r)/build把整个软链接链到内核源码目录或者/usr/src/linux-headers-$(uname -r)然后模块编译的Makefile里写上KDIR : /lib/modules/$(shell uname -r)/build这样才是真正的针对目标内核编译。如果板子没法装kernel headers你需要在PC上准备目标板同版本的内核源码配置好架构和交叉编译工具然后把make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- modules_prepare生成的中间文件打包带到板子上再在板子上编译模块。不过这个流程略微麻烦建议开发阶段直接在板子上安装kernel headers省心很多。7. 移植的实际教训工具链版本与芯片平台匹配最后专门说一个很容易瞎忙活的事情工具链版本和芯片平台的匹配。热词列表里有linux40 飞腾arm交叉编译、RK3576 qt交叉编译环境、arm compiler 5.06u7下载这些都属于这一范畴。飞腾ARM平台的CPU微架构版本在AArch64里相对特殊出现时间晚于基础的Cortex-A53/A72如果Linaro工具链版本过老比如gcc 5.x生成的指令调度可能没有针对新微架构做充分优化编译出来的程序虽然能跑但性能有损失。这种场景下直接用芯片厂商或者麒麟发行版配套的交叉工具链最稳它们已经在具体微架构上做过大量测试。你也可以自己查/proc/cpuinfo看看CPU的part number再对照ARM官方文档确认微架构名称从而选择匹配的编译优化参数。RK3576这类较新的SoC也有类似问题。它的GPU、NPU等都带专有用户态驱动Qt交叉编译时要处理-no-opengl还是接-linuxfb这些显示后端的选择。如果你只是做一个带界面的简单的Qt程序建议用Linux framebuffer或者EGLFS后端避免依赖X11/Wayland的复杂配置。至于arm compiler 5.06u7那是个老牌裸机编译器ARM官方早就不更新了但很多老项目还在用它编译ARM11/Cortex-A系列的裸机代码。这类场景下没有Linux、没有动态库编译产物是一个纯静态的镜像烧写到Flash里直接启动。它的编译参数和Linux用户态程序差异很大网上很多教程把它混在一起讲其实命名里都带arm compiler内核完全不是一回事。如果要做裸机开发一定要和Linux用户态开发分开建目录、分开写文档工具链千万不要混用。8. 我的日常使用建议捋一遍这十来天的学习和实操交叉编译这套东西本身不复杂真正复杂的是环境里的各种隐式依赖和版本错位。我把平时工作的几条习惯总结如下第一工具链尽量固定版本不要随手升级。哪怕只是小版本更新GLIBC版本、GCC代码生成策略变了都可能让整个sysroot变得不稳定。项目里最好把工具链路径、版本、源码hash写进README别人接手能复现。第二sysroot越精简越好。很多人追求大而全把开发板上所有库都打包进sysroot结果宿主机和板子之间库版本稍微不一致编译出来的程序行为就开始漂移。我一般只放入目标程序真正用到的库并用ldd反复核对。第三能用QEMU验证就先用QEMU别每次都等板子。QEMU user模式对调试动态库依赖、跑通启动流程效率极高几分钟就能搞定一轮循环。只有在涉及外设驱动、GPU硬件加速这类无法模拟的场景时才切到真机。第四做Qt类大项目前先去下载厂商SDK的toolchain和sysroot而不是自己折腾。厂商呢一般会把一整条链配好包括Qt库的交叉编译版本、驱动动态库、甚至调试工具。自己从零搭一套虽然学得多但真到了项目交付节奏时间成本往往顶不住。最后留一句实在话交叉编译会者不难难的是你不知道自己踩的坑属于哪一类。希望这篇DAY17的整理能帮你在踩坑之前先看清楚脚下的路。后面如果还有机会我可以再写一篇Qt交叉编译后的国际化与性能调优那也是另一番世界。
返回列表