调试分析之 根据内核报错信息PC指针分析错误

发布时间:2024-08-13  

大家写驱动的时候不知道有没有发现,当我们驱动写错了,发生内核奔溃时,会打印一大堆的报错信息,


如果再返回我们的程序中一行一行代码的检查,既耗费时间,并且有些逻辑上的错误,我们是很难看的出来的,


那我们能不能再这一大堆的报错信息中发现问题的所在呢?


此处我们来模拟一个错误,还是沿用上一篇文章中的驱动代码err_led.c的驱动程序中的代码修改错误,当然大家用其他的驱动代码做测试也可以.


 1  40 static int key_open(struct inode *inode, struct file *file)

 2  41 {

 3  42     printk('<0>function open!nn');

 4  43 

 5  44     base_iomux = 0x43FAC000;

 6  45     MUX_CTL &= ~(0x07 << 0);

 7  46     MUX_CTL |= (0X05 << 0); //设置为ALT5  GPIO3_23 ERR_LED

 8  47 

 9  48     //MUX_CTL

10  49     return 0;

11  50 }


直接让base_iomux = 实际的物理地址,肯定会报错的.


加载编译,如我们所愿,成功的打印出了内核奔溃信息:

e70df5f4055a9a531c1349e91267c9f4_ZdevbS2dVDtolhVpT7DTVg0wEZ89e+b4+GS69W8vfOCdRC872IuloP8BIGNc9hmjNwsAAAAASUVORK5CYII=.png

接下来我们来分析分析是否可以从这些信息中寻得蛛丝马迹呢?让我们一起见证奇迹的时刻.


 1 root@EasyARM-iMX257 ~# echo 1 > /dev/err_led_dev

 2 function open!

 3 

 4 Unable to handle kernel paging request at virtual address 43fac060

 5 //无法访问虚拟地址43fac060,因为驱动访问的都是虚拟地址,而恰恰43fac060这个地址是没有映射的,以无法访问

 6 

 7 pgd = c3b8c000

 8 [43fac060] *pgd=00000000

 9 Internal error: Oops: 5 [#2] PREEMPT

10 Modules linked in: err_led mymsg gpio [   Tainted: G      D     (2.6.31-207-g7286c01 #692)

11 //发生错误时,系统加载的模块有err_led mymsg gpio 这几个

12 

13 PC is at key_open+0x18/0x54 [err_led]

14 //PC就是发生错误的指令地址,发生错误的函数为 key_open,偏移0x18,其实这里已经很明显了

15 

16 LR is at key_open+0x10/0x54 [err_led]

17 //LR寄存器的值

18 

19 pc : []    lr : []    psr: 60000013

20 //发生错误时pc指针的值: bf010128

21 

22 sp : c32c3e70  ip : c046708f  fp : 00095ab0

23 r10: c3b9aae0  r9 : c320  r4 : 00000001

24 r3 : 00000000  r2 : 00000000  r1 : 43facfff  r0 : 43fac000

25 //执行这条错误导致错误时各个寄存器的值

26 

27 Flags: nZCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user

28 Control: 0005317f  Table: 83b8c000  DAC: 000000 limit = 0xc32c2270)

29 

30 //发生错误时,堆栈的信息

31 

32 Stack: (0xc32c2000 c00bb9d0 0000000b c3 c31790c8 00000000 c00bb7fc c380f0a0 c31b6398 c00b66b4 

33 3ea0: c32c3ef8 c3b9aae0 c3885660 c32c3e0b76d4 

34 3ec0: 00000000 c3885660 c32c3ef0 00000000 c32c3ef0 c00c4288 00000000 000001b6 

35 3ee0: 00020241 00000000 00000000 00000000000b 

36 3f00: c3b91005 c380f2200000 00005402 00000036 

37 3f20: 00000000 c00c5698 c31498a0 fffffff7 be880704 c00c5d34 c32c3f84 00 c3879d60 00000003 c380f0a0 c31b6398 00000000 00020241 

38 3f60: 000001b6 ffffff9c 00000000 c0029f24 c3b91000 00000003 00095ab0 c00b6444 

39 3f80: 00000022 00000000 000001b6 000932ac 00000001 00000005 c0029f24 c32c2000 

40 3fa0: 40138000 c0029da0 000001b6 000932000000 

41 3fc0: 000001b6 000932ac 00000001 00000005 00000000 000933f8 40138000 00095ab0 

42 3fe0: 000903ac be8803d8 00035c28 400d110x18/0x54 [err_led]) from [] (chrdev_open+0x1d4/0x1f4)

43 [] (chrdev_open+0x1d4/0x1f4) from [] (__dentry_open+0x18c/0x2ac)

44 [] (__dentry_open+0x18c/0x2ac) from [] (nameidata_to_filp+0x44/0x5c)

45 [] (nameidata_to_filp+0x44/0x5c) from [] (do_filp_open+0x3e4/0x7e8)

46 [] (do_filp_open+0x3e4/0x7e8) from [] (do_sys_open+0x5c/0x114)

47 [114) from [] (ret_fast_syscall+0x0/0x2c)

48 Code: e24dd004 eb40e05c e59f1030 e59f0030 (e5113f9f) 

49 ---[ end trace c4bb5578ca399f8a ]---

50 process '/sbin/getty -L ttymxc0 115200 vt100' (pid 1832) exited. Scheduling for restart.

51 starting pid 1833, tty '': '/sbin/getty -L ttymxc0 115200 vt100'

52 CC) 4.1.2

53 root filesystem built on Tue, 13 Aug 2013 02:31:56 -0700

54 Freescale Semiconductor, Inc.

55 

56 

57 //如果内核的配置选项选择了kernel 的FRAME_POINTER 的话

58 -->kernel hacking

59 - - → - - > kernel debuging (DEBUG_KERNEL)

60 //这儿会打印粗回溯信息,即一系列的内核指针调用信息.

61 Backtrace:


有的时候pc值只会给出列出一个地址,不会说是在那个函数里面



1.根据上面的PC值,找到出错误的地址: System.map


先判断是否属于内核的地址: 看源代码目录下的linux-2.6.31/System.map


 1 root@Lover雪:/home/study/nfs_home/system/linux-2.6.31# vi System.map

 2 1 c0004000 A swapper_pg_dir

 3 2 c0008000 T __init_begin

 4 3 c0008000 T _sinittext

 5 4 c0008000 T stext

 6 5 c0008000 T _stext

 7 6 c0008034 t __enable_mmu

 8 ..........

 9 32128 c04a0d64 b ratelimit.21298

10 32129 c04a0d68 b pipe_version_lock

11 32130 c04a0d68 b pipe_version_rpc_waitqueue

12 32131 c04a0db4 b rsc_table

13 32132 c04a1db4 b rsi_table

14 32133 c04a1eb8 B krb5_seq_lock

15 32134 c04a1ec0 b i.21559

16 32135 c04a1ec8 b wireless_nlevent_queue

17 32136 c04a1ed4 B __bss_stop

18 32137 c04a1ed4 B _end


可以发现内核的地址范围为c0004000 ~ c04a1ed4 ,如果属于这个范围则为内核错误.



如果不属于System.map 里的范围,则它属于insmod加载的程序.]


因为我们的PC值为 bf010128,肯定不属于内核啊,



2.假设它是加载的驱动程序引入的错误,怎么确定是哪一个驱动程序呢?


/proc/kallsyms


先看看加载的驱动程序的函数的地址范围.cat /proc/kallsyms > 1.txt


在开发板上运行cat /proc/kallsyms  > 1.txt


在1.txt中查看PC = bf010128 相近的值

28484 bf0100ec t key_read [err_led]

28485 bf0100ec t $a   [err_led]

28486 bf01010c t $d   [err_led]

28487 bf010110 t key_open [err_led]

28488 bf010110 t $a   [err_led]

28489 bf010154 t $d   [err_led]

28490 bf010164 t $a   [err_led]

28491 bf010248 t $d   [err_led]

28492 bf01024c t key_irq_exit [err_led]

28493 bf01024c t $a   [err_led]

28494 bf0102ac t $d   [err_led]


可以发现:我们这个程序所有的程序的地址,


我们找到与PC值项相近并且小于它的地方,可以发现,我们除错的程序再key_open 里面.


t: 静态函数


T: 全局函数


然后利用这个找到的地址再加上我们的偏移地址,就找到了我们的错误所在.



3.找到了我们的驱动程序err_led.ko反汇编


 1 root@Lover雪:/home/study/nfs_home/module/36_my_proc_prink/test# 

 2 arm-none-linux-gnueabi-objdump  -D err_led.ko > err_led.txt

 3 root@Lover雪:/home/study/nfs_home/module/36_my_proc_prink/test#

 4 vi err_led.txt

 5 下面就是我们err_led.ko 的反汇编代码:

 6 再反汇编代码中找到key_open函数然后在加上偏移0x18

 7 

 8  84 00000110 :

 9  85  110:   e52de004    str lr, [sp, #-4]!

10  86  114:   e59f0038    ldr r0, [pc, #56]   ; 154 <.text+0x154>

11  87  118:   e24dd004    sub sp, sp, #4  ; 0x4

12  88  11c:   ebfffffe    bl  0

13  89  120:   e59f1030    ldr r1, [pc, #48]   ; 158 <.text+0x158>

14  90  124:   e59f0030    ldr r0, [pc, #48]   ; 15c <.text+0x15c>

15  91  128:   e5113f9f    ldr r3, [r1, #-3999]

16  92  12c:   e3c33007    bic r3, r3, #7  ; 0x7   //R3 &= ~(0x07)

17  93  130:   e5013f9f    str r3, [r1, #-3999]

18  94  134:   e5112f9f    ldr r2, [r1, #-3999]

19  95  138:   e59f3020    ldr r3, [pc, #32]   ; 160 <.text+0x160>

20  96  13c:   e3822005    orr r2, r2, #5  ; 0x5    //R3 |= 0x05

21  97  140:   e5830000    str r0, [r3]

22  98  144:   e3a00000    mov r0, #0  ; 0x0



如上面代码所示,很容易就找到了了错误地址.


分析汇编代码:


接下来就是考察汇编功底的时刻了,


根据汇编代码来推出C语言代码:


从上面推出的 R3 &= ~(0x07) 和 R3 |= 0x05,再与程序中匹配:


 1  40 static int key_open(struct inode *inode, struct file *file)

 2  41 {

 3  42     printk('<0>function open!nn');

 4  43 

 5  44     base_iomux = 0x43FAC000;

 6  45     MUX_CTL &= ~(0x07 << 0);

 7  46     MUX_CTL |= (0X05 << 0); //设置为ALT5  GPIO3_23 ERR_LED

 8  47 

 9  48     //MUX_CTL

10  49     return 0;

11  50 }


可以发现,一模一样,有没有.所以我们就得到了MUX_CTL这儿有错误.


根据MUX_CTL自然就可以得到base_iomux这个有错误.


附上驱动程序:err_led.c

  1 #include

  2 #include

  3 #include

  4 #include

  5 #include

  6 #include

  7 #include

  8 #include

  9 #include

 10 #include

 11 #include

 12 #include

 13 #include

 14 

 15 #define Driver_NAME 'err_led_dev'

 16 #define DEVICE_NAME 'err_led_dev'

 17 

 18 static int major = 0;

 19 

 20 //auto to create device node

 21 static struct class *drv_class = NULL;

 22 static struct class_device *drv_class_dev = NULL;

 23 

 24 //寄存器基址;

 25 static unsigned long base_iomux;      //iomux基址 0X 43FA C000 -  0X 43FA FFFF

 26 static unsigned long base_gpio3;    //gpio3      0X 53FA 4000 -  0X 53FA 7FFF

 27 // MUX_CTL模式选择  配置寄存器

 28 #define MUX_CTL  (*(volatile unsigned long *)(base_iomux + 0x0060))

 29 // PAD_CTL GPIO常用功能设置

 30 #define PAD_CTL  (*(volatile unsigned long *)(base_iomux + 0x0270))

 31 // GPIO DR   数据寄存器  DR

 32 #define DR_GPIO3 (*(volatile unsigned long *)(base_gpio3 + 0x0000))

 33 // GPIO GDIR 方向控制寄存器  GDIR

 34 #define GDIR_GPIO3 (*(volatile unsigned long *)(base_gpio3 + 0x0004))

 35 

 36 

 37 extern int printk(const char *fmt, ...);

 38 

 39 static int key_open(struct inode *inode, struct file *file)

 40 {

 41     printk('<0>function open!nn');

 42 

 43     base_iomux = 0x43FAC000;

 44     MUX_CTL &= ~(0x07 << 0);    

 45     MUX_CTL |= (0X05 << 0);    //设置为ALT5  GPIO3_23 ERR_LED

 46 

 47     //MUX_CTL

 48     return 0;

文章来源于:电子工程世界    原文链接
本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>