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

发布者:快乐航程最新更新时间:2024-08-13 来源: cnblogs关键字:调试分析  PC指针 手机看文章 扫描二维码
随时随地手机看文章

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


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


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


此处我们来模拟一个错误,还是沿用上一篇文章中的驱动代码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?imageView2/2/w/1000

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


 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;

[1] [2] [3]
关键字:调试分析  PC指针 引用地址:调试分析之 根据内核报错信息PC指针分析错误

上一篇:调试分析之 根据内核报错信息栈信息分析错误
下一篇:调试分析之 imx257中proc下mymsg及myprintk的实现

推荐阅读最新更新时间:2026-03-25 09:14

调试分析之 根据内核报错信息信息分析错误
错误驱动源文件: 加载错误驱动程序 1 root@EasyARM-iMX257 /mnt/nfs/module/37_debug_err_led# echo 1 /dev/errdule/37_debug_err_led# echo 1 /dev/err_led_dev 2 le kernel paging request at virtual address 43fac060 3 pgd = c3b8c000 4 *pgd=00000000 5 Internal error: Oops: 5 PREEMPT 6 Modules linked in: err_led gpio 7 C
[单片机]
ARM指针寄存器 -程序计数器PC、堆栈指针SP
堆栈是一种具有“后进先出”(LIFO---Last In First Out)特殊访问属性的存储结构。堆栈一般使用RAM 物理资源作为存储体,再加上LIFO 访问接口实现。 堆栈的实现方法: 在随机存储器区划出一块区域作为堆栈区,数据可以一个个顺序地存入(压入)到这个区域之中,这个过程称为‘压栈’(push )。通常用一个指针(堆栈指针 SP---Stack Pointer)实现做一次调整,SP 总指向最后一个压入堆栈的数据所在的数据单元(栈顶)。从堆栈中读取数据时,按照堆栈 指针指向的堆栈单元读取堆栈数据,这个过程叫做 ‘弹出’(pop ),每弹出一个数据,SP 即向相反方向做一次调整,如此就实现了后进先出的原则。 堆栈
[单片机]
9个PLC程序调试及结果分析介绍
一、小车往返运动 用S7-200实现小车往返的自动控制 ,控制过程为按下启动按钮 ,小车从左边往右边(右边往左边运动) 当运动到右边(左边)碰到右边(左边)的行程开关后 小车自动做返回运动,当碰到另一边的行程开关后又做返回运动 。如此的往返运动,直到当按下停车按钮后小车停止运动。 ▲ 电气 接线图 I/O分配表 梯形图程序 PLC 接线图 程序调试及结果分析 ▲控制平台操作面板 当按下SB2即i0.0(鼠标点击i0.0f)接通后,Q0.0接通,小车右行(即指示 灯 Q0.0 亮)。当小车运行碰到右限位开关SQ2即i0.4(用鼠标点击i0.4f, 模拟 SQ2被压下)接通,此时小车左行(指示灯Q0.
[嵌入式]
9个PLC程序<font color='red'>调试</font>及结果<font color='red'>分析</font>介绍
stm32处理器调试模式下运行正常,上电自启动后运行不正常的故障排查分析
最近负责一个项目,用到stm32f4的一款高性能芯片。研发过程中遇到一个很诡异的现象,前前后后折腾了两三天,最后才搞定。由于是新手,经验不足,排故过程很纠结~~ 现象如下: 1.采用JLINK下载程序后,断电让其上电重新启动,发现有时可以正常运行,有时候无法正常运行,大约每两、三次就有一次无法正常上电启动。 2.通过JLINK调试程序,每次均正常运行。太诡异了! 发现问题后开始定位原因。首先考虑是BOOT启动出问题了,stm32f4启动方式分三种:User FLash、SystemFlash和EmbeddedSRAM,通过BOOT0和BOOT1管脚配置。程序正常运行时从User FLash启动,如果BOOT0和BOOT1配
[单片机]
stm32调试模式下运行正常,上电自启动后运行不正常排查分析
最近用到STM32F103的芯片,芯片单步正常,直接上电不能正常工作,加上程序中里面有点bug.然后就各种莫名奇妙。 后面,单步状态把bug找完之后,理论上上电就可以正常跑了。但是总会出现莫名其面的问题。 比如,①单步正常,直接跑就异常。 ② 异常指的是程序不是死机或者hardfault而是不按照预想的逻辑,但是还是看似正常的运行。 ③用镊子复位,其可以正常起来, 百度了一下:https://blog.csdn.net/weibo1230123/article/details/80664466 多数是查时钟复位电源,但是无效。最后发现是外设启动慢导致的。 首先,明白一个道理,单步逻辑对的,那么程序就是对的
[单片机]
利用高速数字分析仪和专用内存软件调试更高速率 DDR 信号
内存 广泛应用于各种设备的单板。而随着电子产品对数据吞吐量的不断提高,内存也在更新换 代,进一步提升了速率,如新一代内存 DDR4,数据信号速率达到了 3.2Gbps。更高速率的内存信号,不仅 JEDEC 规范规定了更严格的测试要求和更多的测试项目,如对于 DDR4 还要求测试固有抖动 Dj,以及信号 的眼图模板测试等,而且对传统的内存测试 软件 中所使用的 DDR 内存信号读写分离方法,也构成了新的挑 战。本文将分析和介绍更高速率的 DDR 内存信号测试所面临的挑战以及力科的应对方案。 一、传统分析软件针对 DDR 信号的读写分离原理 常用的 DDR 内存分析软件一般是通过 DDR 内存的 DQ 数据信号与 DQS 选通信
[测试测量]
利用高速数字<font color='red'>分析</font>仪和专用内存软件<font color='red'>调试</font>更高速率 DDR 信号
便携式功率分析仪设计----键盘设计与校准、调试与测试
键盘操作 一般的测量仪器都可通过按键输入命令对仪器进行测量,按键的种类很多,从机械结构来分有机械接触式的,导电橡胶的等,但无论何种按键都具有一个最基本的特性,那就是能实现触点的通和断,然后通过电路实现电气上的逻辑通和断,从而实现功能的控制,在现代电子测量仪器中一个按键能表示一个使仪器完成某种操作的命令,也可用几个按键组合完成一个特定的命令,还能用一个按键在不同的状态下表示不同的命令,但一般每个按键都有其唯一的代码。CPU通过读代码来识别按键进行处理,按键的排列一般都是矩阵形式,每一个按键都有唯一的行、列位置,所以CPU通过确定按键的行列来确定按键的位置,键盘与CPU的连接方式有两种,一种是利用中断,当有键按下时,按键闭合,键盘板
[测试测量]
便携式功率<font color='red'>分析</font>仪设计----键盘设计与校准、<font color='red'>调试</font>与测试
示波器频域分析在电源调试的应用
  在电源噪声的分析过程中,比较经典的方法是使用示波器观察电源噪声波形并测量其幅值,据此判断电源噪声的来源。但是随着数字器件的电压逐步降低、电流逐步升高,电源设计难度增大,需要使用更加有效的测试手段来评估电源噪声。本文是使用频域方法分析电源噪声的一个案例,在观察时域波形无法定位故障时,通过FFT(快速傅立叶变换)方法进行时频转换,将时域电源噪声波形转换到频域进行分析。电路调试时,从时域和频域两个角度分别来查看信号特征,可以有效地加速调试进程。   在单板调试过程中发现一个网络的电源噪声达到80mv,已经超过器件要求,为了保证器件能够稳定工作必须降低该电源噪声。   在调试该故障前先回顾下电源噪声抑制的原理。如下图所示
[测试测量]
示波器频域<font color='red'>分析</font>在电源<font color='red'>调试</font>的应用
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved