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

实验中用到的源文件

  • first_drv.c
     在first_drv_init函数中使用如下语句故意产生一个段错误,直接使用物理地址(未使用ioremap进行映射)
gpfcon = (volatile unsigned long *)0x56000050; //(volatile unsigned long *)ioremap(0x56000050, 16);
#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 <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/device.h>

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 <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>

/* 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 <on|off>\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 : [<bf000018>]    lr : [<c008d888>]    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
---------------------------- 
[<bf000000>] (first_drv_open+0x0/0x3c [first_drv]) from [<c008d888>] (chrdev_open+0x14c/0x164)
[<c008d73c>] (chrdev_open+0x0/0x164) from [<c0089e48>] (__dentry_open+0x100/0x1e8)
 r8:c3f57dac r7:c0474f20 r6:c008d73c r5:c3f020c0 r4:c3dea500
[<c0089d48>] (__dentry_open+0x0/0x1e8) from [<c0089f64>] (nameidata_to_filp+0x34/0x48)
[<c0089f30>] (nameidata_to_filp+0x0/0x48) from [<c0089fb8>] (do_filp_open+0x40/0x48)
 r4:00000002
[<c0089f78>] (do_filp_open+0x0/0x48) from [<c008a2f4>] (do_sys_open+0x54/0xe4)
 r5:be8f2eb0 r4:00000002
[<c008a2a0>] (do_sys_open+0x0/0xe4) from [<c008a3a8>] (sys_open+0x24/0x28)
[<c008a384>] (sys_open+0x0/0x28) from [<c002bea0>] (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 : [<bf000018>]
# 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 <first_drv_open>:
   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 
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,176评论 5 469
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,190评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,232评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,953评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,879评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,177评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,626评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,295评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,436评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,365评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,414评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,096评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,685评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,771评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,987评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,438评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,032评论 2 341

推荐阅读更多精彩内容