设备驱动调试段错误,根据oops信息确定出错的代码位置,根据栈的信息推出调用关系

实验中用到的源文件

  • first_drv.c
     在first_drv_init函数中使用如下语句故意产生一个段错误,直接使用物理地址(未使用ioremap进行映射)
gpfcon = (volatile unsigned long *)0x56000050; //(volatile unsigned long *)ioremap(0x56000050, 16);
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

static struct class *firstdrv_class;
static struct class_device  *firstdrv_class_dev;

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;


static int first_drv_open(struct inode *inode, struct file *file)
{
    //printk("first_drv_open\n");
    /* 配置GPF4,5,6为输出 */
    *gpfcon &= ~((0x3<<(4*2)) | (0x3<<(5*2)) | (0x3<<(6*2)));
    *gpfcon |= ((0x1<<(4*2)) | (0x1<<(5*2)) | (0x1<<(6*2)));
    return 0;
}

static ssize_t first_drv_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    int val;

    //printk("first_drv_write\n");

    copy_from_user(&val, buf, count); //    copy_to_user();

    if (val == 1)
    {
        // 点灯
        *gpfdat &= ~((1<<4) | (1<<5) | (1<<6));
    }
    else
    {
        // 灭灯
        *gpfdat |= (1<<4) | (1<<5) | (1<<6);
    }
    
    return 0;
}

static struct file_operations first_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   first_drv_open,     
    .write  =   first_drv_write,       
};


int major;
static int first_drv_init(void)
{
    major = register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核

    firstdrv_class = class_create(THIS_MODULE, "firstdrv");

    firstdrv_class_dev = class_device_create(firstdrv_class, NULL, MKDEV(major, 0), NULL, "xyz"); /* /dev/xyz */

    gpfcon = (volatile unsigned long *)0x56000050; //(volatile unsigned long *)ioremap(0x56000050, 16);
    gpfdat = gpfcon + 1;

    return 0;
}

static void first_drv_exit(void)
{
    unregister_chrdev(major, "first_drv"); // 卸载

    class_device_unregister(firstdrv_class_dev);
    class_destroy(firstdrv_class);
    iounmap(gpfcon);
}

module_init(first_drv_init);
module_exit(first_drv_exit);

MODULE_LICENSE("GPL");
  • firstdrvtest.c测试程序

#include 
#include 
#include 
#include 

/* firstdrvtest on
  * firstdrvtest off
  */
int main(int argc, char **argv)
{
    int fd;
    int val = 1;
    fd = open("/dev/xyz", O_RDWR);
    if (fd < 0)
    {
        printf("can't open!\n");
    }
    if (argc != 2)
    {
        printf("Usage :\n");
        printf("%s \n", argv[0]);
        return 0;
    }

    if (strcmp(argv[1], "on") == 0)
    {
        val  = 1;
    }
    else
    {
        val = 0;
    }
    
    write(fd, &val, 4);
    return 0;
}

 装载驱动后执行测试程序失败,打印段错误

# ./firstdrvtest on
Unable to handle kernel paging request at virtual address 56000050
pgd = c3f34000
[56000050] *pgd=00000000
Internal error: Oops: 5 [#1]
Modules linked in: first_drv
CPU: 0    Not tainted  (2.6.22.6 #1)
-----------------------------------------------------
PC is at first_drv_open+0x18/0x3c [first_drv]
--------------------------------------------------------
pc就是发生错误的地址,大多时候pc没有那么多详细的信息,只有个地址值,也就是bf000018
--------------------------------------------------------

LR is at chrdev_open+0x14c/0x164
---------------------------------------------
lr是返回地址
------------------------------------------------
pc : []    lr : []    psr: a0000013
sp : c3d45e88  ip : c3d45e98  fp : c3d45e94
r10: 00000000  r9 : c3d44000  r8 : c3dea500
r7 : 00000000  r6 : 00000000  r5 : c3f020c0  r4 : c06f06c0
r3 : bf000000  r2 : 56000050  r1 : bf000964  r0 : 00000000
执行这条导致错误的指令后的各个寄存器的值
----------------------------------------------------------
Flags: NzCv  IRQs on  FIQs on  Mode SVC_32  Segment user
Control: c000717f  Table: 33f34000  DAC: 00000015
Process firstdrvtest (pid: 780, stack limit = 0xc3d44258)
进程ID号
----------------------------------------------------------
Stack: (0xc3d45e88 to 0xc3d46000)
5e80:                   c3d45ebc c3d45e98 c008d888 bf000010 00000000 c3dea500 
5ea0: c3f020c0 c008d73c c0474f20 c3f57dac c3d45ee4 c3d45ec0 c0089e48 c008d74c 
5ec0: c3dea500 c3d45f04 00000003 ffffff9c c002c044 c3f2b000 c3d45efc c3d45ee8 
5ee0: c0089f64 c0089d58 00000000 00000002 c3d45f68 c3d45f00 c0089fb8 c0089f40 
5f00: c3d45f04 c3f57dac c0474f20 00000000 00000000 c3f35000 00000101 00000001 
5f20: 00000000 c3d44000 c046d388 c046d380 ffffffe8 c3f2b000 c3d45f68 c3d45f48 
5f40: c008a16c c009fc70 00000003 00000000 c3dea500 00000002 be8f2eb0 c3d45f94 
5f60: c3d45f6c c008a2f4 c0089f88 00008520 be8f2ea4 0000860c 00008670 00000005 
5f80: c002c044 4013365c c3d45fa4 c3d45f98 c008a3a8 c008a2b0 00000000 c3d45fa8 
5fa0: c002bea0 c008a394 be8f2ea4 0000860c 00008720 00000002 be8f2eb0 00000001 
5fc0: be8f2ea4 0000860c 00008670 00000002 00008520 00000000 4013365c be8f2e78 
5fe0: 00000000 be8f2e54 0000266c 400c98e0 60000010 00008720 00000000 00000000 
---------------------------
Backtrace:(回溯信息)
内核需要配置此项CONFIG_FRAME_POINTER=y
---------------------------- 
[] (first_drv_open+0x0/0x3c [first_drv]) from [] (chrdev_open+0x14c/0x164)
[] (chrdev_open+0x0/0x164) from [] (__dentry_open+0x100/0x1e8)
 r8:c3f57dac r7:c0474f20 r6:c008d73c r5:c3f020c0 r4:c3dea500
[] (__dentry_open+0x0/0x1e8) from [] (nameidata_to_filp+0x34/0x48)
[] (nameidata_to_filp+0x0/0x48) from [] (do_filp_open+0x40/0x48)
 r4:00000002
[] (do_filp_open+0x0/0x48) from [] (do_sys_open+0x54/0xe4)
 r5:be8f2eb0 r4:00000002
[] (do_sys_open+0x0/0xe4) from [] (sys_open+0x24/0x28)
[] (sys_open+0x0/0x28) from [] (ret_fast_syscall+0x0/0x2c)
Code: e24cb004 e59f1024 e3a00000 e5912000 (e5923000) 
Segmentation fault
# 
  • 根据这些信息定位
    1.pc属于哪个地址,insmod装载的程序还是内核里的程序
     1.1 先看system.map确定是不是内核的函数的地址范围
book@book-desktop:~/system/linux-2.6.22.6$ cat System.map 
c0004000 A swapper_pg_dir 起始地址
c03ca954 B _end
book@book-desktop:~/system/linux-2.6.22.6$ 
  1. 是哪一个加载的程序
     cat /proc/kallsyms从所有函数里面找到一个相近的地址----pc : []
# cat /proc/kallsyms | grep '^b'
bf0000c0 t first_drv_init       [first_drv]
bf00016c t first_drv_exit       [first_drv]
bf000960 b $d   [first_drv]
bf000770 d first_drv_fops       [first_drv]
bf000770 d $d   [first_drv]
bf00003c t first_drv_write      [first_drv]
bf000000 t first_drv_open       [first_drv]
bf000000 t $a   [first_drv]
bf000038 t $d   [first_drv]
bf00003c t $a   [first_drv]
bf0000bc t $d   [first_drv]
bf0000c0 t $a   [first_drv]
bf000140 t $d   [first_drv]
bf00096c b firstdrv_class       [first_drv]
bf000970 b firstdrv_class_dev   [first_drv]
bf00016c t $a   [first_drv]
bf0001b0 t $d   [first_drv]
bf0008cc d $d   [first_drv]
bf000968 b major        [first_drv]
bf000964 b gpfcon       [first_drv]
bf0007e0 d __this_module        [first_drv]
bf0000c0 t init_module  [first_drv]
bf00016c t cleanup_module       [first_drv]
bf000960 b gpfdat       [first_drv]
# 

3.bf000000 t first_drv_open [first_drv]

arm-linux-objdump -D first_drv.ko > first_drv.dis


r2 : 56000050
  18:   e5923000    ldr r3, [r2]这条指令报错

以上是作为模块加载进去的,现在编入内核里面
反汇编内核,直接搜索pc的值,就能找到出错的地址

根据栈信息找出函数的调用关系

1.open函数的栈,sp = c3d45e88 ,一个数值占据4个字节,
调用open的时候,存入4个寄存器
c3d45ebc(fp) c3d45e98(ip) c008d888(lr) bf000010(pc)
根据这个将内核反汇编,查看这个地址的函数是什么c008d888

00000000 :
   0:   e1a0c00d    mov ip, sp
   4:   e92dd800    stmdb   sp!, {fp, ip, lr, pc}
Stack: (0xc3d45e88 to 0xc3d46000)
5e80:                   c3d45ebc c3d45e98 c008d888 bf000010 00000000 c3dea500 
5ea0: c3f020c0 c008d73c c0474f20 c3f57dac c3d45ee4 c3d45ec0 c0089e48 c008d74c 
5ec0: c3dea500 c3d45f04 00000003 ffffff9c c002c044 c3f2b000 c3d45efc c3d45ee8 
5ee0: c0089f64 c0089d58 00000000 00000002 c3d45f68 c3d45f00 c0089fb8 c0089f40 
5f00: c3d45f04 c3f57dac c0474f20 00000000 00000000 c3f35000 00000101 00000001 
5f20: 00000000 c3d44000 c046d388 c046d380 ffffffe8 c3f2b000 c3d45f68 c3d45f48 
5f40: c008a16c c009fc70 00000003 00000000 c3dea500 00000002 be8f2eb0 c3d45f94 
5f60: c3d45f6c c008a2f4 c0089f88 00008520 be8f2ea4 0000860c 00008670 00000005 
5f80: c002c044 4013365c c3d45fa4 c3d45f98 c008a3a8 c008a2b0 00000000 c3d45fa8 
5fa0: c002bea0 c008a394 be8f2ea4 0000860c 00008720 00000002 be8f2eb0 00000001 
5fc0: be8f2ea4 0000860c 00008670 00000002 00008520 00000000 4013365c be8f2e78 
5fe0: 00000000 be8f2e54 0000266c 400c98e0 60000010 00008720 00000000 00000000 

你可能感兴趣的:(设备驱动调试段错误,根据oops信息确定出错的代码位置,根据栈的信息推出调用关系)