原文地址

http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29401328&id=4923447

发生段错误原因就是访问了不该访问的地址,例如访问了不存在的内存地址、访问了系统保护的内存地址、访问了只读的内存地址等。

下面根据Oops信息来分析一下段错误

first_drv.c

#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/init.h>#include <linux/delay.h>#include <asm/uaccess.h>#include <asm/irq.h>#include <asm/io.h>#include <linux/device.h>static struct class *segment_class;static struct device *segment_class_dev;unsigned char *c = NULL;int major;static int segment_test_open(struct inode *inode, struct file *file){*c = 0x34;//printk("segment_test_open success!\n");return 0;}static struct file_operations segment_test_fops = {.owner = THIS_MODULE, /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */.open = segment_test_open,};static int segment_drv_init(void){major = register_chrdev(0, "segment_test", &segment_test_fops); // 注册, 告诉内核segment_class = class_create(THIS_MODULE, "segment_test");segment_class_dev = device_create(segment_class, NULL, MKDEV(major, 0), NULL, "segment");c = (unsigned char *)0x48000000;printk("segment_drv_init success!\n");return 0;}static void segment_drv_exit(void){    device_destroy(segment_class, MKDEV(major,0));class_destroy(segment_class);unregister_chrdev(major, "segment_test");}module_init(segment_drv_init);module_exit(segment_drv_exit);MODULE_LICENSE("GPL");

firstdrvtest.c

#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#include <stdio.h>int main(int argc, char **argv){int fd;int val = 1;fd = open("/dev/segment", O_RDWR);if (fd < 0){printf("can't open!\n");return -1;}close(fd);return 0;}

# insmod first_drv.ko

# ./firstdrvtest

Unable to handle kernel paging request at virtual address 48000000     // 内核使用48000000来访问时发生了错误

pgd = c3b4c000

[48000000] *pgd=00000000

Internal error: Oops: 805 [#1]

Modules linked in: first_drv rt5370sta zd1211rw mac80211

CPU: 0    Not tainted  (2.6.30.4-EmbedSky #1)

PC is at segment_test_open+0x1c/0x28 [first_drv]// PC值

LR is at chrdev_open+0xcc/0x170

pc : []lr : []    psr: a0000013                // 发生错误时各寄存器的值(下面五行)

sp : c3a61e30  ip : c3a61e40  fp : c3a61e3c

r10: c394bc80  r9 : 00000002  r8 : c34b7600

r7 : c3b46100  r6 : c3ab84b0  r5 : c3a62180  r4 : 00000000

r3 : 00000034  r2 : 48000000  r1 : c3b46100  r0 : 00000000

Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  ISA ARM  Segment user

Control: c000717f  Table: 33b4c000  DAC: 00000015

Process firstdrvtest (pid: 637, stack limit = 0xc3a60268)              // 发生错误时当前进程的名称是firstdrvtest

Stack: (0xc3a61e30 to 0xc3a62000)                                                 // 栈

1e20:                                     c3a61e64 c3a61e40 c00a8580 bf0d7010

1e40: c00adba8 00000000 00000000 c3b46100 c3ab84b0 c00a84b4 c3a61e8c c3a61e68

1e60: c00a3a7c c00a84c4 c3b46100 c2c0ae40 00000003 c3af0000 00000026 c3a61ed8

1e80: c3a61eac c3a61e90 c00a3d14 c00a39bc 00000000 c2c0ae40 00000000 00000000

1ea0: c3a61f64 c3a61eb0 c00b0c80 c00a3cc0 c3a61f7c c3a61ec0 c004b714 c006f8b8

1ec0: c3a61efc beb5ad9c 00000000 00000000 c3a63000 c048070c c394bc80 c34b7600

1ee0: c048077c c3a61fb0 00000000 00000101 00000001 00000000 c00441e0 c004b548

1f00: 08100875 c39568a0 c3a7ec00 0000001c 00000000 00001000 00000003 00000003

1f20: 00000000 c3b46100 00000000 c3a60000 c3a61f64 c3a61f40 c00b99b8 00000003

1f40: c3af0000 00000002 beb5ad9c ffffff9c c3a60000 00000000 c3a61f94 c3a61f68

1f60: c00a38d8 c00b0aa0 00000000 40025000 c3a61f9c 0000850c 00000000 000083e0

1f80: 00000005 c0045008 c3a61fa4 c3a61f98 c00a3988 c00a3878 00000000 c3a61fa8

1fa0: c0044e60 c00a3974 0000850c 00000000 00008590 00000002 beb5ad9c 00000001

1fc0: 0000850c 00000000 000083e0 00000005 00000000 00000000 40025000 beb5ac44

1fe0: 00000000 beb5ac28 000084b8 400efd9c 60000010 00008590 00000000 00000000

Backtrace:                                                                          // 回溯信息

[] (segment_test_open+0x0/0x28 [first_drv]) from [] (chrdev_open+0xcc/0x170)

[] (chrdev_open+0x0/0x170) from [] (__dentry_open+0xd0/0x270)

r7:c00a84b4 r6:c3ab84b0 r5:c3b46100 r4:00000000

[] (__dentry_open+0x0/0x270) from [] (nameidata_to_filp+0x64/0x6c)

[] (nameidata_to_filp+0x0/0x6c) from [] (do_filp_open+0x1f0/0x7e8)

r5:00000000 r4:00000000

[] (do_filp_open+0x0/0x7e8) from [] (do_sys_open+0x70/0xe8)

[] (do_sys_open+0x0/0xe8) from [] (sys_open+0x24/0x28)

r8:c0045008 r7:00000005 r6:000083e0 r5:00000000 r4:0000850c

[] (sys_open+0x0/0x28) from [] (ret_fast_syscall+0x0/0x2c)

Code: e59f3010 e3a00000 e5932000 e3a03034 (e5c23000)

---[ end trace d31b8aee70b25c9c ]---

Segmentation fault



上面的这些调试信息包含了很多内容,我们可以根据其中的一部分就可以定位出问题,下面逐一介绍一下。





一、直接确定发生错误的函数

看到这句 “PC is at segment_test_open+0x1c/0x28 [first_drv]”,出现错误时我们最关注的就是PC值,因为它就是发生错误

的指令的地址,这里我们可以看到错误发生在函数 segment_test_open 的0x1c处,0x28代表这个函数的总长度(汇编代码)





二、根据PC值确定发生错误的函数



有时候不会直接告诉你发生在哪个函数,而是只把PC值告诉你:

pc : []

这时你要根据PC值自己找到发生错误的地方,怎么找呢?



现在我们知道发生错误时 PC = 0xbf0d701c,我们首先要确定发生的错误位置是在内核中还是在外面的模块里,

然后根据PC值找出发生的函数及指令。怎么确定?



1. 进入到我们内核源码的根目录下,找到System.map,这个文件指示了所有的内核函数的地址范围,

我们可以观察,发生错误时PC值是不是在这个文件的地址范围内,例如我的这个文件的地址范围是:

c0004000 A swapper_pg_dir  ~~~  c04ec044 B _end



如果不属于System.map里的范围,则它属于insmod加载的驱动程序,这里可以看到bf0d701c属于模块地址



2.知道错误在模块里了,那么怎么确定是哪一个驱动程序?



在开发板上查看:

# cat /proc/kallsyms    // 内核函数、加载的函数的地址,t是静态函数,T是全局函数



从这些信息里找到一个与PC值相近的地址

比如找到了:

00000000 a first_drv.c  [first_drv]

bf0d7000 t $a   [first_drv]

bf0d7000 t segment_test_open    [first_drv]

bf0d7024 t $d   [first_drv]

bf0d7028 t $a   [first_drv]

bf0d7028 t segment_drv_exit     [first_drv]



这里可以看出来,PC=bf0d701c 是属于segment_test_open函数



其实,我们只通过“cat /proc/kallsyms”就可以知道是哪个函数发生了错误,步骤1只是让我们知道这个函数是属于内核的还是模块的





三、通过回溯信息确定发生错误的函数



Backtrace:

[] (segment_test_open+0x0/0x28 [first_drv]) from [] (chrdev_open+0xcc/0x170)

省略好几行



这部分是回溯信息,从最后调用的发生错误的函数层层打印出函数的调用关系,上一行的函数被下一行的调用。

注意:在配置内核时,需要选择 FRAME_POINTER = y 才会有回溯信息,如果没有,可以根据栈信息分析





四、定位发生错误的代码(需要汇编阅读能力)



上面几种方法都只定位了发生错误的函数,怎么定位到是哪一句代码发生了错误呢?



1. 如果发生的错误函数是属于模块的,如我们的这个实例

segment_test_open+0x1c/0x28

这里的0x1c是指汇编代码的地址,所以我们要把这个模块反汇编,然后定位。



# arm-none-linux-gnueabi-objdump -D first_drv.ko > first_drv.dis

打开first_drv.dis有下面这一段:



00000000 :

0:   e1a0c00d        mov     ip, sp

4:   e92dd800        push    {fp, ip, lr, pc}

8:   e24cb004        sub     fp, ip, #4      ; 0x4

c:   e59f3010        ldr     r3, [pc, #16]   ; 24

10:   e3a00000        mov     r0, #0  ; 0x0

14:   e5932000        ldr     r2, [r3]

18:   e3a03034        mov     r3, #52 ; 0x34

1c:   e5c23000        strb    r3, [r2]

20:   e89da800        ldm     sp, {fp, sp, pc}

24:   00000000        .word   0x00000000



这里代码的实际地址都要加上偏移地址 bf0d7000,发生错误的那句代码是:
1c:   e5c23000        strb    r3, [r2]
根据我们的C语言代码可以看出这里是把0x34赋给变量时产生错误,产生错误的原因是加载模块初始化时赋给的一个地址非法:
c = (unsigned char *)0x48000000;

我们这里的程序比较短,可以一眼看出来,如果代码很长,就可以根据发生错误的位置,大概确定代码的位置,
然后再去看代码和汇编,这里要求比较高的汇编阅读能力



2. 如果发生的错误函数是属于内核的



这个时候和发生在模块里类似,不过这里要反汇编整个内核:

# arm-none-linux-gnueabi-objdump -D vmlinux > vmlinux.dis

打开vmlinux.dis,然后直接查找地址bf0d7000,接下来像上面一样分析代码



关于上面的打印信息,还有栈那一段没有讲,放到下一篇博文说

linux驱动调试--段错误之oops信息分析相关推荐

  1. Linux驱动段错误,linux驱动调试--段错误之oops信息分析

    下面根据Oops信息来分析一下段错误 first_drv.c 点击(此处)折叠或打开 #include #include #include #include #include #include #in ...

  2. linux驱动调试--段错误之栈信息分析

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29401328&id=4923529 接着上一篇来分析一下Oop ...

  3. linux驱动获取函数失败信息,linux驱动调试--段错误之栈信息分析

    接着上一篇来分析一下Oops的栈 s3c2440平台 关于调试源码和整个Oops信息请参考上一篇博文,这里只再次贴出关于栈的信息 Stack: (0xc3a61e30 to 0xc3a62000) 1 ...

  4. linux 捕获sigsegv信息如何生成core文件,[转]Linux下的段错误产生的原因及调试方法Core Dump...

    简而言之,产生段错误就是访问了错误的内存段,一般是你没有权限,或者根本就不存在对应的物理内存,尤其常见的是访问0地址. 一般来说,段错误就是指访问的内存超出了系统所给这个程序的内存空间,通常这个值是由 ...

  5. Linux环境下段错误的产生原因及调试方法小结

    From:http://www.cnblogs.com/panfeng412/archive/2011/11/06/2237857.html 最近在Linux环境下做C语言项目,由于是在一个原有项目基 ...

  6. [转]Linux环境下段错误的产生原因及调试方法小结

    最近在Linux环境下做C语言项目,由于是在一个原有项目基础之上进行二次开发,而且项目工程庞大复杂,出现了不少问题,其中遇到最多.花费时间最长的问题就是著名的"段错误"(Segme ...

  7. [转] Linux C语言 段错误bug的调试

    原来看过一次,后来当自己有段错误的时候,才想起来这个很有用.如果不用他的方法,段错误很恶心的,不好找出来的. 下面就是原文,不过 最初出处不得而知 ======================= 复制 ...

  8. Linux下的段错误产生的原因及调试方法-转

    分类: Linux--Ubuntu入门级 重学C/C++2011-10-19 22:13 332人阅读 评论(0) 收藏 举报 因为你调用了glibc的fputs 检查你传进去的char* +++++ ...

  9. linux程序运行段错误,Linux下的段错误产生的原因及调试方法

    Linux下的段错误产生的原因及调试方法 简而言之,产生段错误就是访问了错误的内存段,一般是你没有权限,或者根本就不存在对应的物理内存,尤其常见的是访问0地址. 一般来 说,段错误就是指访问的内存超出 ...

最新文章

  1. 使用Word2010灵活掌握文档结构
  2. 刘政:别着急颠覆传统数据分析人才培养,先将落脚点放在实处
  3. ORA-600 [kddummy_blkchk] [18038] 一例
  4. java swing 表格不显示_JAVA SWING 表头不显示问题
  5. 使用字符串解析的方式完成计算器的设计思路
  6. 获取窗口上指定控件集合 2012-08-22 16:14 498人阅读 评论(0) 收藏...
  7. java中的foreach
  8. centos6架设dhcp服务器提供两个网段的IP地址分配
  9. Javascript数据类型,类型转换
  10. Java程序员如何写好一份个人求职简历
  11. (Rock, Paper, Scissors, Lizard and Spock)sheldon谢耳朵独创的剪刀,布,石头,蜥蜴,史波克 用代码实现
  12. vscode缩放代码_Visual Studio Code 缩放设置
  13. 计算机基础应用在线免费答题,计算机应用基础简答题附答案.doc
  14. 如何打造智能世界的数据底座?深耕华为云大数据,畅享价值最大化
  15. set、env、export的区别
  16. 深圳/河南/天津/山东产品经理国际认证NPDP招生简章
  17. win10 家庭中文版内存占用过高
  18. 开源一个后台基于bmob云端的社交app
  19. 地图上道路编号中的G S X Y
  20. python可视化词云图WordCloud

热门文章

  1. 最新CSGO国服能取回皮肤的国内开箱网站推荐大全
  2. java g1 详解_JAVA垃圾收集算法总结以及CMS、G1算法详解
  3. 论文笔记《Are You Talking to Me? Reasoned Visual Dialog Generation through Adversarial Learning》
  4. 汉语是思维广阔敏捷的语言
  5. Google Maps Android API
  6. Win11设置鼠标箭头图案的方法教程
  7. HAProxy配置详解
  8. Hive中各种日期格式转换方法总结
  9. 低级程序员和高级程序员的区别在于?
  10. java script实训心得_javascript实训报告总结.docx