🔍 目录
- 交叉编译工具链 & 常用工具参数详解
- Linux 内核下载、编译流程与产物一览
- 内核调试日志与 Backtrace:从配置到实践
- KASAN(Kernel Address SANitizer)的开启与使用流程
- ARM 寄存器图鉴:数据 vs. 地址如何识别
- Linux 社区源码 & Patch 查看最佳实践
1️⃣ 交叉编译工具链 & 常用工具参数详解
嵌入式 Linux 最常用到的一套工具链,以 GCC 为例,前缀通常是 arm-linux-gnueabihf-
。下面列出关键工具及常见参数。
工具 | 用途 | 常用参数 |
---|---|---|
arm-linux-gnueabihf-gcc | 交叉编译器 | -march=armv7-a 指定架构-mfpu=neon 指定浮点单元-mfloat-abi=hard 硬浮点-O2 优化等级-g 生成调试信息-Wall 全部警告 |
arm-linux-gnueabihf-gdb | 调试用户态程序 / 内核(Remote) | -q 静默启动-ex "target remote :1234" 连接 QEMU/GDBstub-ex "set arch arm" 设置架构-ex "file vmlinux" 预装内核符号 |
arm-linux-gnueabihf-addr2line | 符号地址 → 源码位置 | -e <vmlinux> 指定可执行/符号文件-f 打印函数名-i 打印 inline 路径-C demangle C++ 名称 |
arm-linux-gnueabihf-objdump | 反汇编 / 查看符号 | -d 反汇编所有可执行段-D 反汇编全部段(含数据)-l 打印源文件行号-S 源+汇一体-t 列出符号表 |
arm-linux-gnueabihf-readelf | 查看 ELF 头 & 段信息 | -h ELF Header-S 段表-r 重定位表-s 符号表 |
示例:
# 把内核崩溃地址还原到源码 arm-linux-gnueabihf-addr2line -e vmlinux -f -i -C 0xc08a2345
2️⃣ Linux 内核下载、编译流程与产物一览
2.1 获取源码
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
# 可切到稳定版或长期支持版
git checkout v6.4
2.2 配置与编译
# 1) 生成默认配置
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- defconfig
# 2) 开启常用调试项(可选)
scripts/config --enable DEBUG_INFO
scripts/config --enable FRAME_POINTER
scripts/config --enable KALLSYMS
# 3) 重新生成 .config,并编译
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc)
# 4) 安装模块(可选)
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- INSTALL_MOD_PATH=./mods modules_install
2.3 常见产物说明
文件/目录 | 说明 |
---|---|
vmlinux | ELF 格式、带符号表的未压缩内核镜像 |
System.map | 符号 ↔ 地址映射表,用于 crash 时地址还原 |
arch/arm/boot/zImage | 压缩内核镜像(U-Boot 加载用) |
arch/arm/boot/Image | 未压缩内核镜像 |
arch/arm/boot/dts/ | 各板级支持包的设备树二进制(.dtb) |
modules/ | 安装好的模块文件(.ko),位于 lib/modules/ |
include/config/ | 编译时的配置快照 |
3️⃣ 内核调试日志与 Backtrace:从配置到实践
小白常见疑惑是:为什么开启了调试选项仍然看不到完整的 Backtrace?下面一一拆解。
3.1 必要的内核配置
在 make menuconfig → Kernel hacking
中务必开启:
Compile the kernel with debug info (DEBUG_INFO)
Keep frame pointers (FRAME_POINTER)
— 支持栈回溯Enable loadable module support (MODVERSIONS)
+KALLSYMS
— 模块 & 内核符号Kernel debugging (DEBUG_KERNEL)
Compile the kernel with debug info (KGDB)
(可选,用于 KGDB 远程单步)Magic SysRq key
— 在 panic 后手工触发 dump
3.2 Bootargs & earlyprintk
在引导加载器(如 U-Boot)中加入:
console=ttyAMA0,115200 earlyprintk=serial,ttyAMA0,115200 loglevel=7 nokaslr
earlyprintk
:在 very-early 阶段打印日志loglevel=7
:输出所有级别(0–7)nokaslr
:关闭地址随机化,符号映射更稳定
3.3 常见日志查看
# 本地查看
dmesg --level=err,warn,info
# 实时跟踪
tail -f /dev/kmsg
# ftrace 追踪函数调用
echo function_graph > /sys/kernel/debug/tracing/current_tracer
cat /sys/kernel/debug/tracing/trace
3.4 Backtrace 实战
当出现 Oops
或 BUG
,终端会打印类似:
BUG: unable to handle kernel NULL pointer dereference at 00000000deadbeef
R0 : 0000000000000000 R1 : ffffffff81c23456
LR : ffffffff810ab123 PC : ffffffff810abcde (my_func+0x20/0x58)
Stack: 0000ffff82a9f1b0 0000ffff82a9f1d0 ...
Call trace:
[<ffffffff810abcde>] my_func+0x20/0x58
[<ffffffff810def01>] another_func+0x100/0x110
还原地址 → 源码:
arm-linux-gnueabihf-addr2line -e vmlinux -f -i -C ffffffff810abcde
4️⃣ KASAN(Kernel Address SANitizer)的开启与使用流程
KASAN 可捕获 Use-After-Free / 越界读写等难以定位的内存错误。
4.1 配置步骤
在 make menuconfig → Kernel hacking
中:
-
KASAN: runtime memory debugger
→ 选择- Generic KASAN(大多数架构)
- 或 Software Tag-Based KASAN(ARM64*)
-
记得:
DEBUG_INFO
、FRAME_POINTER
均需开启。
4.2 编译 & 启动
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j$(nproc)
# 引导参数加上:kasan=on kasan.policy=0
4.3 使用流程
- 复现崩溃场景:执行可能越界 / Use-After-Free 的测试用例。
- 观察 KASAN 报告:
================================================================== BUG: KASAN: use-after-free in my_alloc_func+0x34/0x60 Read of size 4 at addr ffff800012345678 by task test_process/1234 ... Use $CWD/vmlinux + addr → 找到源代码上下文
- 定位调用栈 & 源码:
arm-linux-gnueabihf-addr2line -e vmlinux -f -i -C ffffffff81012345
- 修复 & 回归测试:根据报告修改内存分配/释放逻辑,重新验证无报错。
5️⃣ ARM 寄存器图鉴:数据 vs. 地址如何识别
寄存器 | 用途 | 存“地址”还是“数据”? |
---|---|---|
r0–r3 | 参数寄存器 / 临时 | 可存数据,也可存指针(第一参数通常是指针) |
r4–r11 | 保留寄存器(callee-saved) | 多为局部变量或地址 |
sp (r13) | 栈顶指针 | 地址 |
lr (r14) | 返回地址 | 地址 |
pc (r15) | 程序计数器 | 地址 |
cpsr | 程序状态寄存器 | 标志位 / 状态 |
- 识别技巧
- 看值域:Kernel 地址通常在
0xffff0000_00000000
以上,数据(如小整数)远小于此。 - 查看调用约定:ABI 规定,函数前 4 个参数从 r0–r3 传递,若被调用函数期望指针,则相应寄存器为地址。
- 结合反汇编上下文:通过
objdump -d -l
查看汇编指令,若寄存器用作[r0, #offset]
,它即为地址基址。
- 看值域:Kernel 地址通常在
6️⃣ Linux 社区源码 & Patch 查看最佳实践
6.1 在线平台
- git.kernel.org:主仓库浏览与搜索
- elixir.bootlin.com:代码 + 文档注释一体化
- patchwork.kernel.org:Patch 提交/审核状态
6.2 Git 本地查询
# 查某行是谁加的
git blame net/core/neighbour.c -L 100,120
# 查谁改过 bucket 初始化
git log -S 'state->bucket' -p net/core/neighbour.c
# 查看具体提交
git show <commit-id>
6.3 定期关注邮件列表
netdev@vger.kernel.org
,linux-kernel@vger.kernel.org
:订阅获得最新 Patchscripts/get_maintainer.pl <file>
:快速获取相关维护者邮件列表
📌 小结
本文从工具链、编译产物、日志配置、KASAN、寄存器、社区协作全方位展开,既有参数详解、操作范例,也有定位思路。掌握这些内容,能够让你在遇到嵌入式 Linux 内核问题时,快速定位、调试并提交高质量 Patch。
若需落地实战脚本、图文并茂版本或发布到博客平台,欢迎继续交流!