[第2课时 · 4/8] Kernel 常见编译产物

0. 这一节要解决什么

一次内核编译结束后,构建目录里会出现十几个文件。哪些要部署到板子、哪些只是调试用、哪些根本不该手动碰——先把名称和用途对上,部署与排障才不会拿错文件。

读完这一节,应能对每个产物说出:它是什么、谁产生、去哪里。

1. 产物总表

产物 是什么 去向 / 用途
Image ARM64 裸内核镜像 U-Boot 用 booti 加载的对象(第 1 课时 6/6 手动启动的就是它)
vmlinux 带符号的 ELF 内核 调试分析用;不能直接 booti
System.map 内核符号地址表 对照 oops / panic 日志里的调用栈地址
*.dtb 编译后的设备树 描述板级硬件,交给内核
*.dtbo overlay 的编译结果 叠加在基础 DTB 之上的片段
*.ko 可加载内核模块 安装到 rootfs,开机后按需加载
extboot.img boot 分区镜像(本 SDK) 打包 Image + DTB + extlinux 配置

标准路径:

kernel/arch/arm64/boot/Image
kernel/arch/arm64/boot/dts/rockchip/rk3576-firefly-roc-rk3576-pc.dtb
kernel/vmlinux
kernel/extboot.img        ./build.sh extboot 生成
kernel/extboot/           同一打包结果的目录形式

2. 三个「看起来像内核」的文件

文件 格式 能否被 U-Boot booti 加载
vmlinux ELF,带符号 :cross_mark: 体积大、格式不对,用于调试(gdb、addr2line)
Image 裸二进制 :white_check_mark: ARM64 内核的标准启动形态
Image.gz gzip 压缩 视 U-Boot 支持而定;需先解压

判断文件类型不要靠文件名猜,用 file:

file arch/arm64/boot/Image arch/arm64/boot/dts/rockchip/*.dtb vmlinux
# Image  → data            (裸二进制)
# *.dtb  → device tree blob
# vmlinux→ ELF 64-bit LSB executable, ARM aarch64

3. .ko:编进内核与编成模块的区别

内核编译讲过 Y/M/N,落到产物上就是:

  • Y → 对应代码链进 Image,开机即有,根设备驱动必须这样
  • M → 生成独立 .ko 文件,位于 drivers/**/*.ko,需安装到 rootfs(make modules_install 或 SDK 打包时自动完成)
# 板子上看已加载的模块
lsmod
modinfo xxx.ko      # 查看模块信息(在 PC 上)
insmod /lib/modules/.../xxx.ko

排障含义:某设备不工作时,先分清「驱动没编」/「编成模块但没加载」/「probe 失败」三种情况——对应手段分别是查 .config、lsmod、dmesg。

4. extboot.img:boot 分区的封装

本 SDK 的板级配置带 USE_EXTBOOT=y,单独编译内核的命令 ./build.sh extboot 产物不是散的 Image + dtb,而是一个 boot 分区镜像:

kernel/extboot.img            ← 可整分区烧写(upgrade_tool di -boot)
kernel/extboot/               ← 同一内容的目录形式
    ├── Image
    ├── <board>.dtb
    └── extlinux/extlinux.conf   ← U-Boot 读它决定加载哪个内核与 DTB

两种部署方式等价:把 kernel/extboot/ 下的文件拷到板子 /boot/ 后重启,或把 extboot.img 烧进 boot 分区。细节在第 7、8 节展开。

观念:extboot.img 只是封装容器,不是启动链路中的一棒。封装与启动阶段是两回事。

5. Lab:反编译 DTB,眼见为实

# device-tree-compiler 已在 SDK 依赖列表中
dtc -I dtb -O dts kernel/extboot/rk3576-firefly-roc-rk3576-pc.dtb | less

对照观察:

  1. 反编译结果与源码 arch/arm64/boot/dts/rockchip/rk3576-firefly-roc-rk3576-pc.dts 的 model 一致
  2. dtsi 里的节点全部展开进了 DTB——include 只是源码层的组织手段,编译后是一个整体
  3. /chosen 节点此时可能还没有 bootargs——它是 U-Boot 交接时才写进去的

6. 本节小结

  • 产物分三类:启动用的(Image、DTB、extboot.img)、调试用的(vmlinux、System.map)、运行期加载的(.ko)
  • file 判类型;booti 只吃裸 Image
  • extboot.img 是本 SDK 的 boot 分区封装:Image + DTB + extlinux 配置
  • 封装 ≠ 启动阶段

7. 本节思考

Q1:为什么 vmlinux 明明是"完整内核"却不能用来启动?

查看答案

vmlinux 是 ELF 格式、带完整符号与调试信息,体积可达数十 MB,入口与加载布局是给调试器用的。内核启动要求把裸机器码放到固定物理地址直接跳转执行——Image 正是从 vmlinux 去掉符号、按启动约定裁剪出的二进制。U-Boot 的 booti 只认后者的格式与布局。

Q2:一个驱动编成了 .ko,板子启动后该设备没出现。按顺序检查什么?

查看答案

① .ko 是否在 rootfs 里(find /lib/modules -name 'xxx.ko');② 是否已加载(lsmod),没有则 modprobe xxx 并看报错;③ 加载后 dmesg 里有没有 probe 成功或失败的日志;④ 若 probe 失败,回到设备树核对节点与资源(6/8 的三条件)。注意与 Y 情况区分:若驱动编进了 Image,则根本不存在 .ko。

Q3:extboot.img 和第 1 课时讲的 boot.img 是什么关系?

查看答案

两者都是「boot 分区镜像」这一层封装的名字,本质相同:把内核启动所需的对象(Image、DTB、加载配置)打包成一个可整分区烧写的镜像。区别在组织方式——本 SDK 用 USE_EXTBOOT=y 走 extlinux 风格,产物命名为 extboot.img,内部是 extlinux/extlinux.conf 描述加载哪个内核与 DTB。名字随 SDK 而变,封装的角色不变。