在定位某个内核 oops 问题中,对 arm 架构 oops 信息中的 Code 字段输出信息的内容不太明白,查看内核 Documentation 文档得到如下信息:
“Code” 之后的十六进制字节可能(在某些架构上)有一些当前指令之前的指令以及
当前指令和之后的指令
上述解释并不容易理解,于是阅读了下代码并编写了一个内核模块进行验证,在本文中记录一下。
static void __dump_instr(const char *lvl, struct pt_regs *regs)
{
unsigned long addr = instruction_pointer(regs);
char str[sizeof("00000000 ") * 5 + 2 + 1], *p = str;
int i;
for (i = -4; i < 1; i++) {
unsigned int val, bad;
bad = get_user(val, &((u32 *)addr)[i]);
if (!bad)
p += sprintf(p, i == 0 ? "(%08x) " : "%08x ", val);
else {
p += sprintf(p, "bad PC value");
break;
}
}
printk("%sCode: %s\n", lvl, str);
}
此代码首先访问 pt_regs 结构,从中拿到 pc 指向的地址 addr,然后获取此地址前 4 个指令的值,最后打印的内容为 addr 指向地址的指令值。
测试模块代码如下:
#include
#include
static uint64_t *vaddr = (uint64_t *)0x61b8cba6;
static int __init my_init(void)
{
printk("*vaddr is %llu\n", *vaddr);
printk(KERN_INFO "Hello from Hello Module\\n");
return 0;
}
static void __exit my_exit(void)
{
*vaddr = 0xdeadbeefdeadbeef;
printk(KERN_INFO "Bye from Hello Module\\n");
}
module_init(my_init);
module_exit(my_exit);
MODULE_DESCRIPTION("Sample Hello World Module");
MODULE_LICENSE("GPL");
加载此模块时触发的内核 oops 部分信息如下:
[ 9304.644233][ 0] [<ffffffbffc1e6010>] my_init+0x10/0x40 [test]
[ 9304.649964][ 0] [<ffffffc000082988>] do_one_initcall+0xc8/0x1a8
[ 9304.655869][ 0] [<ffffffc000155e78>] do_init_module+0x5c/0x1b4
[ 9304.661687][ 0] [<ffffffc00010c830>] load_module+0x1a48/0x212c
[ 9304.667504][ 0] [<ffffffc00010d160>] SyS_finit_module+0xac/0xd4
[ 9304.673408][ 0] [<ffffffc000085c70>] el0_svc_naked+0x24/0x28
[ 9304.679052][ 0] Code: d29974c1 f2ac3701 a9bf7bfd 910003fd (f9400021)
相关代码反汇编信息如下:
0000000000000000 :
0: d29974c1 mov x1, #0xcba6 // #52134
4: f2ac3701 movk x1, #0x61b8, lsl #16
8: a9bf7bfd stp x29, x30, [sp,#-16]!
c: 910003fd mov x29, sp
10: f9400021 ldr x1, [x1]
14: 580000e0 ldr x0, 30
18: 94000000 bl 0
1c: 580000e0 ldr x0, 38
20: 94000000 bl 0
24: 52800000 mov w0, #0x0 // #0
28: a8c17bfd ldp x29, x30, [sp],#16
2c: d65f03c0 ret
内核堆栈打印,crash 发生在 my_init+0x10 偏移处,此处的代码如下:
10: f9400021 ldr x1, [x1]
可以看到,指令的内容为 f9400021,正对应内核 oops 中 Code 打印括号内的值。Code 中的其它打印的指令内容,对应如下几个指令:
0: d29974c1 mov x1, #0xcba6 // #52134
4: f2ac3701 movk x1, #0x61b8, lsl #16
8: a9bf7bfd stp x29, x30, [sp,#-16]!
c: 910003fd mov x29, sp
与上文的分析一致,说明分析正确。