内核打印应用程序出错信息,DEBUG_USER

前言

在 Linux 系统中,运行一个应用程序,突然提示段错误,并停止运行

# ./crash.out 
Segmentation fault

如果这个时候操作系统能多提示点错误信息,那将会缩短我们 debug 的时间。
core dump 就是一个办法,可以查看我之前写的一篇文章《core dump》
今天我们来介绍另外一种方法,那就是内核编译选项 DEBUG_USER。

DEBUG_USER

在这里插入图片描述
该编译选项位于 > Kernel hacking > arm Debugging,含义是:详细的用户故障信息。

当用户程序由于异常而崩溃时,内核可以打印一条简短的消息来说明问题所在。这有时对调试很有帮助,但对生产系统没有任何作用。大多数人应该在这里说 N。
此外,你需要在内核命令行上传递 user_debug=N 来启用此特性。N 由以下和组成:

1 - 未定义的指令事件
2 - 系统调用
4 - 无效的数据中止
8 - sigsecv 故障
16 - SIGBUS 故障

测试

实验环境:ARM Linux 32bit,qemu

crash.c

#include <stdio.h>
#include <unistd.h>

int main()
{
	int *p = NULL;

    printf("*p = %d\n", *p);

	return 0;
}

Makefile

CC=/home/liyongjun/project/board/buildroot/Vexpress_2/host/bin/arm-linux-gcc

crash:
	${CC} crash.c -o crash.out -g -no-pie

cp:
	cp crash.out ~/tftp

clean:
	rm *.out

设置内核的命令行参数,添加 user_debug=0xff

# cat /proc/cmdline 
console=ttyAMA0,115200 rootwait root=/dev/mmcblk0 user_debug=0xff

将编译好的程序拷贝到 ARM Linux,为方便调试,关闭地址随机化,参考《ASLR 和 PIE》,然后执行代码

# tftp -gr crash.out 192.168.31.223
# chmod +x crash.out
# echo 0 > /proc/sys/kernel/randomize_va_space
# ./crash.out 
8<--- cut here ---
crash.out: unhandled page fault (11) at 0x00000000, code 0x017
[00000000] *pgd=611d0831, *pte=00000000, *ppte=00000000
CPU: 0 PID: 131 Comm: crash.out Not tainted 6.1.44 #1
Hardware name: ARM-Versatile Express
PC is at 0x10444
LR is at 0x76eb8868
pc : [<00010444>]    lr : [<76eb8868>]    psr: 60080010
sp : 7efffcf8  ip : 7efffd80  fp : 7efffd04
r10: 76ffece0  r9 : 7efffe7c  r8 : 00000000
r7 : 00011ed8  r6 : 00011ed8  r5 : 00000001  r4 : 7efffe74
r3 : 00000000  r2 : 7efffe7c  r1 : 7efffe74  r0 : 00000001
Flags: nZCv  IRQs on  FIQs on  Mode USER_32  ISA ARM  Segment user
Control: 10c5387d  Table: 61b74059  DAC: 00000055
Segmentation fault

程序执行出现段错误,显示 PC 位于 0x10444
我们去 host 主机,反汇编,查看 0x10444 附近的指令是什么

$ /home/liyongjun/project/board/buildroot/Vexpress_2/host/bin/arm-linux-objdump -d crash.out
...
0001042c <main>:
   1042c:       e92d4800        push    {fp, lr}
   10430:       e28db004        add     fp, sp, #4
   10434:       e24dd008        sub     sp, sp, #8
   10438:       e3a03000        mov     r3, #0
   1043c:       e50b3008        str     r3, [fp, #-8]
   10440:       e51b3008        ldr     r3, [fp, #-8]
   10444:       e5933000        ldr     r3, [r3]
   10448:       e1a01003        mov     r1, r3
   1044c:       e3000504        movw    r0, #1284       @ 0x504
   10450:       e3400001        movt    r0, #1
   10454:       ebffffad        bl      10310 <printf@plt>
   10458:       e3a03000        mov     r3, #0
   1045c:       e1a00003        mov     r0, r3
   10460:       e24bd004        sub     sp, fp, #4
   10464:       e8bd8800        pop     {fp, pc}

PC 位于 0x10444,说明代码在执行前一条指令(0x10440)时出问题了,该指令是 ldr 指令,ldr 指令的作用是将内存地址中的数据存入寄存器中,而内存地址的值为[fp, #-8],再往前两条指令

   10438:       e3a03000        mov     r3, #0
   1043c:       e50b3008        str     r3, [fp, #-8]

作用是将立即数 0 赋值给 r3,然后再将 r3 的值赋值给 [fp, #-8],所以 0x10440 处 [fp, #-8] 的值为 0,从 0 地址处取数据,超出了该应用程序所能访问的段的范围,于是发生了段错误。

objdump -S

或者我们直接使用 objdump -S 选项,来显示 C 源码和汇编的混合代码

int main()
{
   1042c:       e92d4800        push    {fp, lr}
   10430:       e28db004        add     fp, sp, #4
   10434:       e24dd008        sub     sp, sp, #8
        int *p = NULL;
   10438:       e3a03000        mov     r3, #0
   1043c:       e50b3008        str     r3, [fp, #-8]

    printf("*p = %d\n", *p);
   10440:       e51b3008        ldr     r3, [fp, #-8]
   10444:       e5933000        ldr     r3, [r3]
   10448:       e1a01003        mov     r1, r3
   1044c:       e3000504        movw    r0, #1284       @ 0x504
   10450:       e3400001        movt    r0, #1
   10454:       ebffffad        bl      10310 <printf@plt>

        return 0;
   10458:       e3a03000        mov     r3, #0
   1045c:       e1a00003        mov     r0, r3
   10460:       e24bd004        sub     sp, fp, #4
   10464:       e8bd8800        pop     {fp, pc}

出错指令对应的 C 代码为 printf("*p = %d\n", *p);,结合前面指针 p 的值为 NULL,可以快速定位到是访问空指针产生的段错误。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Li-Yongjun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值