使用 Buildroot 交叉编译 Vitetris 到 ARM 开发板的完整实践与经验总结
最近翻出很久以前买的 Luckfox pico mini板子,这玩意买回来电都没通过吃灰了好几年。
这几天我尝试使用 Buildroot 将 Vitetris 游戏交叉编译到我的 ARM 开发板上。
这个过程涉及多次试错、Makefile 调整和交叉编译的理解,最终成功完成。
现在我将整个过程整理如下,希望对同样做交叉编译的朋友有所帮助。
1. 初步验证:Hello World
在正式编译 Vitetris 之前,我先验证交叉编译链是否可用。
- 编写一个最简单的 hello.c:
#include <stdio.h>
int main() {
printf("Hello ARM!\n");
return 0;
}- 使用 Buildroot 提供的交叉编译链编译:
arm-linux-gcc -o hello hello.c- 用
file检查生成文件:
file hello
# 输出:
# ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV)✅ 成功生成 ARM 可执行文件,可在开发板上运行,输出 “Hello ARM!”。
通过这个小实验,我确认了交叉编译链可用,并能正确生成目标架构的 ELF 文件。
2. Vitetris 编译问题
初次在 Buildroot 中编译 Vitetris 后,将生成的可执行文件拷贝到 ARM 开发板上运行时,报错:
/usr/bin/tetris: line 1: syntax error: unexpected "|"使用 file 命令检查可执行文件:
file tetris
# 输出:
# ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2发现可执行文件是 x86_64 架构,而开发板是 ARM。说明编译过程中没有使用交叉编译链,而是使用了本机 gcc。
3. 问题分析
- Buildroot 提供了 ARM 交叉编译链。
- Vitetris 源码顶层 Makefile 中 没有显式定义 CC,直接使用
$(CC)。 - 当顶层 Makefile 进入
src目录调用子 Makefile 时,如果不显式传递CC,子 Makefile 会使用系统默认 gcc,导致生成错误架构的可执行文件。
4. 解决过程
4.1 修改 vitetris.mk
原来调用子目录 Make 的命令:
$(MAKE) -C $(@D)修改为:
$(MAKE) CC="$(TARGET_CC)" CFLAGS="$(TARGET_CFLAGS)" LDFLAGS="$(TARGET_LDFLAGS)" -C $(@D)这样顶层 Makefile 的 $(CC) 会被交叉编译链覆盖,并传递到子 Makefile。
4.2 子 Makefile 继承问题
重新编译后,出现:
main.o: file not recognized: file format not recognized
collect2: error: ld returned 1 exit status原因是子 Makefile 仍然使用系统默认 gcc,没有继承父 Make 的 CC。
4.3 显式传递 CC
在顶层 Makefile 调用 src 目录时显式传递:
cd src; $(MAKE) CC="$(CC)" CFLAGS="$(CCFLAGS)" LDFLAGS="$(LDFLAGS)" tetris子 Makefile 中的 $(CC) 就会使用传递过来的交叉编译链。
5. 编译验证
使用 file 检查最终生成的可执行文件:
file sysdrv/source/buildroot/buildroot-2023.02.6/output/target/usr/bin/tetris
# 输出:
# ELF 32-bit LSB pie executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, not stripped✅ 成功生成 ARM 32-bit 可执行文件,可直接在开发板上运行。
6. 知识点总结
交叉编译链必须显式传递给所有子 Makefile:
- 在父 Makefile 中设置
CC="$(TARGET_CC)"、CFLAGS="$(TARGET_CFLAGS)"等,并传递到子 Makefile。 - 否则子 Makefile 默认使用本机 gcc,会生成错误架构的可执行文件。
- 在父 Makefile 中设置
Makefile 中 CC 的继承规则:
- 子 Makefile 不会自动继承父 Makefile 的变量值(除非通过环境变量)。
- 使用
$(MAKE) CC="$(CC)" ... -C subdir可以显式传递。
检查架构和库类型:
file <executable>是最直接检查生成文件架构的方法。- 注意 Buildroot 默认使用 uClibc 动态链接库。
交叉编译调试技巧:
- 小程序先验证交叉编译链是否生效。
- 对复杂项目,先理解 Makefile 层级和变量传递。
- 编译报错时,检查每个对象文件是否是正确架构。
优化:
- 最终生成可执行文件可使用
strip去掉调试符号,减小体积。 - Makefile 可统一管理交叉编译参数,避免重复传递。
- 最终生成可执行文件可使用
7. 收获与经验
通过这次实践,我深入理解了:
- Makefile 变量作用域和继承机制
- Buildroot 的交叉编译流程
- 如何排查架构不匹配问题
- 子 Makefile 与父 Makefile 的交互
- 实战中通过
file检查 ELF 架构的重要性
总之,这次尝试不仅让我成功将 Vitetris 移植到 ARM 开发板,也让我对交叉编译和复杂 Makefile 的工作机制有了系统的理解。
系列文章
- 使用 Buildroot 交叉编译 Vitetris 到 Luckfox ARM 开发板的完整实践与经验总结【当前文章】
- Luckfox Pico Mini 入门学习笔记:GPIO LED 驱动与设备树实践