0. 这一节要解决什么
编译内核的机器是 x86_64 的 PC(或虚拟机),运行内核的板子是 ARM64——CPU 指令集完全不同,PC 上的 gcc 编出的程序板子根本跑不了。本节回答:怎样让 x86 主机产出 ARM64 的内核,以及手工 make 与 SDK 脚本两种方式的关系。
1. 三方关系
编译主机 (x86_64) ──调用──→ 交叉工具链 ──输出──→ 目标板 (ARM64)
运行 make / 脚本 aarch64-linux-gnu-gcc 加载并执行 Image
在主机上运行,产出 ARM64 指令
交叉编译不是「在 PC 上编译然后拷过去」那么简单——编译器本身决定生成哪种指令集。普通 gcc 生成 x86_64 指令;交叉工具链前缀为 aarch64-linux-gnu- 的 gcc 生成 ARM64 指令。
2. 两个参数各管一件事
| 参数 | 作用 | 错误后果 |
|---|---|---|
ARCH=arm64 |
选择目标架构:决定使用 arch/arm64/ 下的代码与 defconfig |
找不到 defconfig,或编出 x86 内核 |
CROSS_COMPILE=aarch64-linux-gnu- |
指定工具链前缀:make 会调用 aarch64-linux-gnu-gcc 等 |
报 aarch64-linux-gnu-gcc: No such file,或用了主机 gcc |
记忆法:ARCH 管「用哪套架构的源码」,CROSS_COMPILE 管「用哪套编译器」。
3. 工具链从哪里来
两种来源:
- 系统安装:
sudo apt install gcc-aarch64-linux-gnu(版本可能偏旧) - SDK 自带:在
prebuilts/目录下,版本与 SDK 匹配
用 SDK 入口(build.sh)编译时,脚本会自动把工具链路径加入环境,不需要手工设置 CROSS_COMPILE;这也是推荐用 SDK 入口的原因之一。
4. 两种方式
手工方式——理解原理用,在 kernel/ 目录内执行:
cd kernel
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- rockchip_linux_defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- menuconfig # 可选
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- -j$(nproc) Image dtbs
产物在 arch/arm64/boot/Image 与 arch/arm64/boot/dts/rockchip/*.dtb。
SDK 方式——课程实际使用,在 SDK 根目录执行:
./build.sh lunch # 选择 firefly_rk3576_roc-rk3576-pc_ubuntu_defconfig
./build.sh extboot # 编译内核并生成 boot 分区镜像 kernel/extboot.img
extboot 目标会完成「配置内核 → 编译 → 打包 boot 分区镜像」一整段,产物在 kernel/extboot.img 与 kernel/extboot/ 目录。
5. 增量编译与清理
- 增量:直接重跑
./build.sh extboot,Kbuild 只重编改动过的文件,第二次通常只需几十秒 - 清理内核:
./build.sh clean kernel;全量清理./build.sh cleanall - 何时必须 clean:切换 defconfig / 工具链 / 板级配置之后,残留的
.o与.config可能导致产物错乱——出现诡异错误时,先 clean 再编是标准动作 - 并行度:
-j$(nproc)用满所有核;内存小于 8G 时适当调小,避免 OOM
6. 常见报错对照
| 报错 | 原因 | 处理 |
|---|---|---|
aarch64-linux-gnu-gcc: No such file or directory |
工具链不在 PATH | 改用 SDK 入口,或给 CROSS_COMPILE 配绝对路径 |
arch/arm64/configs/xxx: No such file |
ARCH 未设或 defconfig 名不对 |
核对 ARCH=arm64 与配置文件名 |
编译到一半 Killed / cc1: out of memory |
并行度过高,内存耗尽 | 减小 -j 值 |
| 产物时间戳是旧的、改动没生效 | 增量编译未识别 / 残留状态 | ./build.sh clean kernel 后重编 |
7. Lab:两种方式各跑一遍,记录证据
# 1) 手工编一个 dtb(快,适合验证工具链)
cd kernel
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- rockchip_linux_defconfig
make ARCH=arm64 CROSS_COMPILE=aarch64-linux-gnu- rk3576-firefly-roc-rk3576-pc.dtb
ls -l arch/arm64/boot/dts/rockchip/rk3576-firefly-roc-rk3576-pc.dtb
# 2) SDK 全流程
cd <SDK>
./build.sh lunch # 选 firefly_rk3576_roc-rk3576-pc_ubuntu_defconfig
time ./build.sh extboot # 记录耗时
ls -l kernel/extboot.img kernel/extboot/
要交的证据:两条产物的 ls -l 输出(含时间戳)与 time 耗时。
8. 本节小结
- 交叉编译的三方:x86 主机跑工具,
aarch64-工具链产 ARM64 指令,板子只负责运行 ARCH选架构源码,CROSS_COMPILE选编译器;SDK 入口会自动备好两者- 手工 make 用于理解原理;课程与实际开发用
./build.sh extboot - 增量编译快;切配置后先 clean;报错先对表
9. 本节思考
Q1:为什么不能直接在板子上编译内核?
查看答案
技术上可行(板子上有 gcc 就能自编译),但实际不可取:内核全量编译需要数 GB 内存与大量 CPU 时间,RK3576 的算力与内存远不如 x86 主机,编译一次可能要数小时甚至 OOM。交叉编译把「重计算」放在主机、「轻运行」放在板子,是嵌入式开发的标准分工。
Q2:手工 make 时漏掉 ARCH=arm64,会发生什么?为什么?
查看答案
make 按主机架构(x86_64)处理:会去 arch/x86/ 找 defconfig(报 No such file),即使运气好编下去,生成的也是 x86_64 内核,板子无法执行。因为内核构建系统用 ARCH 决定架构目录、头文件与指令集相关的全部选择,默认值就是当前主机架构。
Q3:./build.sh extboot 比「手工 make Image dtbs」多做的是什么?
查看答案
多了「准备板级配置 + 打包」两段:按板级入口设置 defconfig 与配置片段生成 .config、选定 RK_KERNEL_DTS_NAME 指定的 DTS,并把 Image、DTB 与 extlinux 配置打包成 boot 分区镜像 kernel/extboot.img。SDK 脚本是便捷入口,不是另一套编译系统——底层仍是 Kbuild 在干活。