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

first_drv.c

点击(此处)折叠或打开

#include

#include

#include

#include

#include

#include

#include

#include

#include

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

#include

#include

#include

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驱动段错误,linux驱动调试--段错误之oops信息分析相关推荐

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

    原文地址 http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=29401328&id=4923447 发生段错误原因就是访问 ...

  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 C语言 段错误bug的调试

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

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

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

  6. linux内存不足+段错误,Linux系统内存错误产生的原因及调试方法(段错误|core dumped)[转]...

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

  7. linux at24c 前几个字节错误,Linux下的段错误产生的原因及调试方法

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

  8. linux驱动程序设计21 Linux设备驱动的调试

    本章导读 "工欲善其事,必先利其器",为了方便进行Linux设备驱动的开发和调试,建立良好的开发环境很重 要,还要使用必要的工具软件以及掌握常用的调试技巧等. 21.1节讲解了Li ...

  9. 嵌入式Linux(5):驱动开发网络调试驱动设备的Linux系统移植

    驱动开发之网络调试驱动设备的Linux系统移植 1.Uboot移植到开发板 uboot移植 2.开发板网络通讯 nfs命令 tftp命令 3.Linux移植到开发板 4.BusyBox 构建根文件系统 ...

最新文章

  1. VMware vSphere四种迁移类型的区别与适应场景
  2. java jni 参数_Java 与 JNI 互传数据的那些事
  3. docker image镜像的发布
  4. python 使用socks5 设置全局代理
  5. dsst依赖opencv
  6. python下载地址-python下载网址
  7. 专属微信二维码python制作_如何利用Python制作简单的公众号二维码关注图
  8. 河南淅川高考成绩查询2021,2021年河南高考成绩查询网址,河南高考成绩查询系统时间安排...
  9. Android自定义标题栏
  10. Win7 U盘安装Ubuntu16.04 双系统详细教程
  11. 健康管理系统案列/APP/小程序/网站
  12. WindowsXP_KB909667_x86_ENU
  13. GitLab配置ssh key:gitlab add an ssh key
  14. web入门 命令执行 web53-web55
  15. 03 TI OMAPL138E Linux移植 (Davinci) (资源获取+从串口启动UBoot+从网络启动Linux与文件系统)
  16. 51nod 1113 矩阵快速幂【裸题】【内含黑科技】
  17. 我的CSDN现在没有C币,没办法下载
  18. xshell5不能用必须应用到最新的更新或使用新版本
  19. 【NGINX入门指北】 基础篇
  20. 数据库(十)-单表查询(1)条件查询

热门文章

  1. mysql通信协议的半双工机制理解
  2. What’s the difference between system.web and system.webServer?
  3. git强行覆盖master分支
  4. 学习 WebService 第五步:在Local创建测试用WebService(WSDL)
  5. 自写小函数处理 javascript 0.3*0.2 浮点类型相乘问题
  6. android图像处理(3) 底片效果
  7. ListView优化机制及滑动时数据时出现的数据错乱重复问题
  8. 一本好书-《精益创业》
  9. pku 1639 Picnic Planning 最小度限制生成树
  10. 关于SSM项目中配置文件的一些心得