应用调试(三)oops

目录

  • 应用调试(三)oops

    • 引入
    • 配置内核打开用户oops
      • CONFIG_DEBUG_USER
      • user_debug
      • 设置启动参数测试
    • 打印用户堆栈
      • 分析栈
    • main的调用


title: 应用调试(三)oops

date: 2019/01/19 12:06:58

toc: true

---

应用调试(三)oops

引入

在驱动程序调试中,发生段错误后内核打印出oops信息,包括pc值,寄存器值和栈信息

Unable to handle kernel paging request at virtual address 56000050
.....

但是我们再应用程序故意引入一个错误(在地址0的地方写数据),只是提示段错误,没有信息打印,那么如何打开这个选项呢?

配置内核打开用户oops

搜索字符Unable to handle kernel,可以在arch\arm\mm\fault.c找到

__do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
          struct pt_regs *regs)
{
...
    printk(KERN_ALERT
        "Unable to handle kernel %s at virtual address %08lx\n",
        (addr < PAGE_SIZE) ? "NULL pointer dereference" :
        "paging request", addr);
    die("Oops", regs, fsr);
...
}

也可以搜索下内核态的调用位置看到

void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
{
    struct task_struct *tsk = current;
    struct mm_struct *mm = tsk->active_mm;

    /*
     * If we are in kernel mode at this point, we
     * have no context to handle this fault with.
     */
    if (user_mode(regs))
        __do_user_fault(tsk, addr, fsr, SIGSEGV, SEGV_MAPERR, regs);
    else
        __do_kernel_fault(mm, addr, fsr, regs);
}

同时在下方就能看到打印用户态的oops

static void
__do_user_fault(struct task_struct *tsk, unsigned long addr,
        unsigned int fsr, unsigned int sig, int code,
        struct pt_regs *regs)
{
    struct siginfo si;

#ifdef CONFIG_DEBUG_USER
    if (user_debug & UDBG_SEGV) {
        printk(KERN_DEBUG "%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",
               tsk->comm, sig, addr, fsr);
        show_pte(tsk->mm, addr);
        show_regs(regs);
    }
#endif

    tsk->thread.address = addr;
    tsk->thread.error_code = fsr;
    tsk->thread.trap_no = 14;
    si.si_signo = sig;
    si.si_errno = 0;
    si.si_code = code;
    si.si_addr = (void __user *)addr;
    force_sig_info(sig, &si, tsk);
}

从代码上就可以看到需要两个处理

  • CONFIG_DEBUG_USER配置需要打开
  • user_debug变量的设置

CONFIG_DEBUG_USER

搜索内核这个配置项DEBUG_USER,已经打开了

  │ Symbol: DEBUG_USER [=y]                                                                                             │
  │ Prompt: Verbose user fault messages                                                                                 │
  │   Defined at arch/arm/Kconfig.debug:18                                                                              │
  │   Location:                                                                                                         │
  │     -> Kernel hacking  

user_debug

搜索变量,可以看到如下,很明显可以通过设置启动参数arch\arm\kernel\traps.c

#ifdef CONFIG_DEBUG_USER
unsigned int user_debug;

static int __init user_debug_setup(char *str)
{
    get_option(&str, &user_debug);
    return 1;
}
__setup("user_debug=", user_debug_setup);
#endif

设置启动参数测试

set  bootargs noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0  user_debug=0xff
boot
mount -t nfs -o nolock,vers=2 192.168.95.222:/home/book/stu /mnt

运行错误的程序,可以看到打印寄存器和pc值

# ./test_debug
a = 0x12
pgd = c2cac000
[00000000] *pgd=32cca031, *pte=00000000, *ppte=00000000

Pid: 792, comm:           test_debug
CPU: 0    Not tainted  (2.6.22.6 #9)
PC is at 0x84ac
LR is at 0x84d0
pc : [<000084ac>]    lr : [<000084d0>]    psr: 60000010
sp : bed7ae40  ip : bed7ae54  fp : bed7ae50
r10: 4013365c  r9 : 00000000  r8 : 00008514
r7 : 00000001  r6 : 000085cc  r5 : 00008568  r4 : bed7aec4
r3 : 00000012  r2 : 00000000  r1 : 00001000  r0 : 00000000
Flags: nZCv  IRQs on  FIQs on  Mode USER_32  Segment user
Control: c000717f  Table: 32cac000  DAC: 00000015
[<c002cd1c>] (show_regs+0x0/0x4c) from [<c0031a98>] (__do_user_fault+0x5c/0xa4)
 r4:c06c30a0
[<c0031a3c>] (__do_user_fault+0x0/0xa4) from [<c0031d38>] (do_page_fault+0x1dc/0x20c)
 r7:c0026520 r6:c3333860 r5:c06c30a0 r4:ffffffec
[<c0031b5c>] (do_page_fault+0x0/0x20c) from [<c002b224>] (do_DataAbort+0x3c/0xa0)
[<c002b1e8>] (do_DataAbort+0x0/0xa0) from [<c002be48>] (ret_from_exception+0x0/0x10)
Exception stack(0xc2ca1fb0 to 0xc2ca1ff8)
1fa0:                                     00000000 00001000 00000000 00000012
1fc0: bed7aec4 00008568 000085cc 00000001 00008514 00000000 4013365c bed7ae50
1fe0: bed7ae54 bed7ae40 000084d0 000084ac 60000010 ffffffff
 r8:00008514 r7:00000001 r6:000085cc r5:00008568 r4:c039bfc8
Segmentation fault

这里其实是有stack的,但这个栈实际上寄存器的值而非程序的栈,可以在notepad++上双击数字看到高亮的

Exception stack(0xc2ca1fb0 to 0xc2ca1ff8)
                                            r0
1fa0:                                     00000000 00001000 00000000 00000012
1fc0: bed7aec4 00008568 000085cc 00000001 00008514 00000000 4013365c bed7ae50
                                            psr
1fe0: bed7ae54 bed7ae40 000084d0 000084ac 60000010 ffffffff
 r8:00008514 r7:00000001 r6:000085cc r5:00008568 r4:c039bfc8

打印用户堆栈

可以看到内核态打印栈

__do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
          struct pt_regs *regs)
    >die("Oops", regs, fsr);
        >show_stack_log_lvl(current, regs->sp, regs, KERN_EMERG)
            >dump_mem("Stack: ", log_lvl, sp,THREAD_SIZE + (unsigned long)tinfo);
            >show_trace_log_lvl(tsk, (unsigned long *)sp, regs, log_lvl);

试了下直接调用内核的打印栈的方式并不能成功,哈哈

添加代码为
    {
        printk("Stack by die: \n");
        die("Oops", regs, fsr);
    }
只会提示这个

Stack by die:
Internal error: Oops: 817 [#1]

我们这里就手动复制到用户态打印好了更改代码如下

static void
__do_user_fault(struct task_struct *tsk, unsigned long addr,
        unsigned int fsr, unsigned int sig, int code,
        struct pt_regs *regs)
{
    struct siginfo si;

#ifdef CONFIG_DEBUG_USER
    if (user_debug & UDBG_SEGV) {
        printk(KERN_DEBUG "%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",
               tsk->comm, sig, addr, fsr);
        show_pte(tsk->mm, addr);
        show_regs(regs);

    }
#endif

    /////////////////////////////////////////////////////////////////////////////
    if(0)
    {
        printk("Stack by die: \n");
        die("Oops", regs, fsr);
    }
    if (1)
    {
        unsigned long i=0,val=0;
        printk("Stack: \n");
        while(i<1024)
        {
           /* copy_from_user()只是用来检测该地址是否有效,如有效,便获取地址数据,否则break */
           if(copy_from_user(&val, (const void __user *)(regs->ARM_sp+i*4), 4))
               break;
           printk("%08x ",val);
           i++;
           if(i%8==0) printk("\n");
        }
        printk("\n END of Stack\n");
    }
    /////////////////////////////////////////////////////////////////////////////
    tsk->thread.address = addr;
    tsk->thread.error_code = fsr;
    tsk->thread.trap_no = 14;
    si.si_signo = sig;
    si.si_errno = 0;
    si.si_code = code;
    si.si_addr = (void __user *)addr;
    force_sig_info(sig, &si, tsk);
}

可以看到打印了栈了

# /mnt/code/test_debug
a = 0x12
pgd = c2cc4000
[00000000] *pgd=32c82031, *pte=00000000, *ppte=00000000

Pid: 789, comm:           test_debug
CPU: 0    Not tainted  (2.6.22.6 #6)
PC is at 0x84ac
LR is at 0x84d0
pc : [<000084ac>]    lr : [<000084d0>]    psr: 60000010
sp : be87ee50  ip : be87ee64  fp : be87ee60
r10: 4013365c  r9 : 00000000  r8 : 00008514
r7 : 00000001  r6 : 000085cc  r5 : 00008568  r4 : be87eed4
r3 : 00000012  r2 : 00000000  r1 : 00001000  r0 : 00000000
Flags: nZCv  IRQs on  FIQs on  Mode USER_32  Segment user
Control: c000717f  Table: 32cc4000  DAC: 00000015
[<c002cd1c>] (show_regs+0x0/0x4c) from [<c0031a9c>] (__do_user_fault+0x60/0x148)
 r4:c04a8800
[<c0031a3c>] (__do_user_fault+0x0/0x148) from [<c0031ddc>] (do_page_fault+0x1dc/0x20c)
[<c0031c00>] (do_page_fault+0x0/0x20c) from [<c002b224>] (do_DataAbort+0x3c/0xa0)
[<c002b1e8>] (do_DataAbort+0x0/0xa0) from [<c002be48>] (ret_from_exception+0x0/0x10)
Exception stack(0xc2cb1fb0 to 0xc2cb1ff8)
1fa0:                                     00000000 00001000 00000000 00000012
1fc0: be87eed4 00008568 000085cc 00000001 00008514 00000000 4013365c be87ee60
1fe0: be87ee64 be87ee50 000084d0 000084ac 60000010 ffffffff
 r8:00008514 r7:00000001 r6:000085cc r5:00008568 r4:c039bfc8
Stack:
00000000 be87ee74 be87ee64 000084d0 000084a0 00000000 be87ee88 be87ee78
000084f0 000084c4 00000000 be87eea8 be87ee8c 00008554 000084e4 00000000
00000012 be87eed4 00000001 00000000 be87eeac 40034f14 00008524 00000000
00000000 0000839c 00000000 00000000 4001d594 000083c4 000085cc 4000c02c
be87eed4 be87ef7f 00000000 be87ef94 be87ef9e be87efa5 be87efb0 be87efd3
be87efe1 00000000 00000010 00000003 00000006 00001000 00000011 00000064
00000003 00008034 00000004 00000020 00000005 00000006 00000007 40000000
00000008 00000000 00000009 0000839c 0000000b 00000000 0000000c 00000000
0000000d 00000000 0000000e 00000000 00000017 00000000 0000000f be87ef7b
00000000 00000000 76000000 2f006c34 2f746e6d 65646f63 7365742f 65645f74
00677562 52455355 6f6f723d 4f480074 2f3d454d 52455400 74763d4d 00323031
48544150 62732f3d 2f3a6e69 2f727375 6e696273 69622f3a 752f3a6e 622f7273
53006e69 4c4c4548 69622f3d 68732f6e 44575000 2f002f3d 2f746e6d 65646f63
7365742f 65645f74 00677562 00000000
 END of Stack
Segmentation fault

分析栈

同样可以先反汇编arm-linux-objdump -D test_debug > test_debug.dis

PC is at 0x84ac
LR is at 0x84d0

Stack:
00000000 be87ee74 be87ee64 000084d0 000084a0 00000000 be87ee88 be87ee78
C_sp                        ldr            ↑ B_sp
000084f0 000084c4 00000000 be87eea8 be87ee8c 00008554 000084e4 00000000
ldr             ↑ A_sp                          ldr          ↑ main_sp
00000012 be87eed4 00000001 00000000 be87eeac 40034f14 00008524 00000000
                                                ldr          ↑
00000000 0000839c 00000000 00000000 4001d594 000083c4 000085cc 4000c02c
be87eed4 be87ef7f 00000000 be87ef94 be87ef9e be87efa5 be87efb0 be87efd3

这里的mani函数最后会返回40034f14这个动态库,动态库不太好分析,我们重新编译为静态链接分析下main被谁调用

main的调用

在动态库中,可以查看 /proc/pidxxx/maps,看到程序动态库的地址,但是这个函数我们直接段错误退出了所以无法查看,可以看下别的pid(shell)的

# cat /proc/772/maps
00008000-000bf000 r-xp 00000000 1f:03 646        /bin/busybox
000c7000-000c8000 rw-p 000b7000 1f:03 646        /bin/busybox
000c8000-000ec000 rwxp 000c8000 00:00 0          [heap]
40000000-40015000 r-xp 00000000 1f:03 733        /lib/ld-2.3.6.so
40015000-40017000 rw-p 40015000 00:00 0
4001d000-4001e000 rw-p 00015000 1f:03 733        /lib/ld-2.3.6.so
4001e000-40023000 r-xp 00000000 1f:03 691        /lib/libcrypt-2.3.6.so
40023000-4002a000 ---p 00005000 1f:03 691        /lib/libcrypt-2.3.6.so
4002a000-4002b000 rw-p 00004000 1f:03 691        /lib/libcrypt-2.3.6.so
4002b000-40052000 rw-p 4002b000 00:00 0
40052000-400f9000 r-xp 00000000 1f:03 708        /lib/libm-2.3.6.so
400f9000-40101000 ---p 000a7000 1f:03 708        /lib/libm-2.3.6.so
40101000-40102000 rw-p 000a7000 1f:03 708        /lib/libm-2.3.6.so
40102000-4020d000 r-xp 00000000 1f:03 734        /lib/libc-2.3.6.so
4020d000-40215000 ---p 0010b000 1f:03 734        /lib/libc-2.3.6.so
40215000-40219000 rw-p 0010b000 1f:03 734        /lib/libc-2.3.6.so
40219000-4021b000 rw-p 40219000 00:00 0
bed05000-bed1a000 rwxp bed05000 00:00 0          [stack]

那么我们可以静态编译这个文件,看看被谁调用

arm-linux-gcc -static -o test_debug_static test_debug.c
arm-linux-objdump -D test_debug_static > test_debug_static.dis

运行后打印了错误信息

# /mnt/code/test_debug_static
<Physical Layer error>
<Physical Layer error>
a = 0x12
pgd = c2cd4000
[00000000] *pgd=32c94031, *pte=00000000, *ppte=00000000
Pid: 789, comm:      test_debug_stat
CPU: 0    Not tainted  (2.6.22.6 #6)
PC is at 0x81e0
LR is at 0x8204
pc : [<000081e0>]    lr : [<00008204>]    psr: 60000010
.....
Stack:
00000000 beeeac94 beeeac84 00008204 000081d4 00000000 beeeaca8 beeeac98
C_Sp                       ldr               B_Sp
00008224 000081f8 00000000 beeeacc8 beeeacac 00008288 00008218 00000000
ldr               A_Sp                        ldr               main_Sp
00000012 beeeaec4 00000001 00000000 beeeaccc 000084ac 00008258 756e694c
                                             ldr             ↑
00000078 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 6f6e2800
0029656e 00000000 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 2e320000
32322e36 0000362e 00000000 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000000 00000000 00000000 00000000 23000000
61532036 614a2074 3931206e 3a333120 303a3630 53432032 30322054 00003931
.......
 END of Stack
Segmentation fault

跳过一些栈,直接看到

00008248 <main>:
可以看到main的返回 000084ac

0000829c <__libc_start_main>:
    84a4:   e1a0e00f    mov lr, pc              ;这个就是main
    84ac:   eb0000fc    bl  88a4 <exit>

也就是最终的关系是

__libc_start_main
    > main
    > exit

原文地址:https://www.cnblogs.com/zongzi10010/p/10291673.html

时间: 2024-11-12 06:56:50

应用调试(三)oops的相关文章

phone 调试三种工具

1. Phonegap桌面开发工具 Phonegap Desktop-App与 手机客户端调试工具PhoneGap Developer App 此工具方便.快捷.自动.可以在真机中查看 无法设置断点.无法调试本地功能. 要手机和电脑在同一个wifi下才可调试. 2. Ripple Emulator 方便.快捷. 无法设置断点.无法调试本地功能. 3. weinre 比较麻烦,但是可以调试本地功能.

XE6 /XE8 &amp; IOS开发之免证书真机调试三步走,生成iPA文件并安装到其它苹果设备上

XE6 & IOS开发之免证书真机调试(1):颁发属于自己的App签名证书(有图有真相) XE6 & IOS开发之免证书真机调试(2):连接真机并运行App(有图有真相) XE6 & IOS开发之免证书真机调试(3):生成iPA文件并安装到其它苹果设备上(有图有真相) XE8 & IOS开发之免费证书真机调试:开发证书.AppID.开发授权profile的申请,附Debug真机调试演示(XCode7 Beta版或以上版本适用,有图有真相)

《驱动调试 - 根据oops的栈信息,确定函数调用过程》

1.上章的oops栈信息如下图所示: 9fe0: 代表最初的栈顶SP寄存器位置 9e80:代表函数出错的SP寄存器位置 2.我们先来分析上图的栈信息,又是怎样的过程呢? 2.1内核主要是通过STMDB和LDMIA汇编命令来入栈和出栈 (STMDB和LDMIA汇编命令参考: http://www.cnblogs.com/lifexy/p/7363208.html) 内核每进入一个函数就会通过STMDB,将上个函数的内容值存入栈顶sp,然后栈顶sp-4. 当内核的某个函数出问题时,内核便通过LDMI

pr_debug、dev_dbg等动态调试三

内核版本:Linux-3.14 作者:彭东林 邮箱:[email protected] 如果没有使用CONFIG_DYNAMIC_DEBUG,那么就需要定义DEBUG,那么此时pr_debug就退化为了printk. 如果定义了CONFIG_DYNAMIC_DEBUG,下面有几种方法: 参考内核文档:Documentation/dynamic-debug-howto.txt Introduction ============ This document describes how to use

《Linux Device Drivers》第四章 调试技术——note

1.本章知识点 内核中和调试相关的选项 CONFIG_DEBUG_KERNEL CONFIG_DEBUG_SLAB CONFIG_DEBUG_PAGEALLOC CONFIG_DEBUG_SPINLOCK CONFIG_DEBUG_SPINLOCK_SLEEP CONFIG_INIT_DEBUG CONFIG_DEBUG_INFO CONFIG_MAGIC_SYSRQ CONFIG_DEBUG_STACKOVERFLOW CONFIG_DEBUG_STACK_USAGE CONFIG_KALLS

js断点调试心得

虽然网上已经有多的数不清的调试教程了,但仍然没有发现哪篇文章写的通俗易懂,索性自己尝试写写自己的一些使用习惯或者说是心得,希望对那些还不是很懂得使用断点调试的孩子有一些帮助(大神请无视~). 1.断点调试是啥?难不难? 断点调试其实并不是多么复杂的一件事,简单的理解无外呼就是打开浏览器,打开sources找到js文件,在行号上点一下罢了.操作起来似乎很简单,其实很多人纠结的是,是在哪里打断点?(我们先看一个断点截图,以chrome浏览器的断点为例) 步骤记住没? 用chrome浏览器打开页面 →

断点调试

js断点调试心得 虽然网上已经有多的数不清的调试教程了,但仍然没有发现哪篇文章写的通俗易懂,索性自己尝试写写自己的一些使用习惯或者说是心得,希望对那些还不是很懂得使用断点调试的孩子有一些帮助(大神请无视~). 1.断点调试是啥?难不难? 断点调试其实并不是多么复杂的一件事,简单的理解无外呼就是打开浏览器,打开sources找到js文件,在行号上点一下罢了.操作起来似乎很简单,其实很多人纠结的是,是在哪里打断点?(我们先看一个断点截图,以chrome浏览器的断点为例) 步骤记住没? 用chrome

13 年的 Bug 调试经验总结

在<Learning From Your Bugs>一文中,我写了关于我是如何追踪我所遇到的一些最有趣的bug.最近,我回顾了我所有的194个条目(从13岁开始),看看有什么经验教训是我可以学习的.下面是我总结的最重要的经验教训,包括编码,测试和调试三个方面.编码下面这些都是我经历过的会导致难点bug的问题:1.事件顺序.在处理事件时,提出下列问题会很有成效:事件可以以不同的顺序到达吗?如果我们没有接收到此事件会怎么样?如果此事件接连发生两次会怎么样?哪怕通常不会发生,但系统(或交互系统)其他

JS 断点调试心得

1.断点调试是啥?难不难? 断点调试其实并不是多么复杂的一件事,简单的理解无外呼就是打开浏览器,打开sources找到js文件,在行号上点一下罢了.操作起来似乎很简单,其实很多人纠结的是,是在哪里打断点?(我们先看一个断点截图,以chrome浏览器的断点为例) 步骤记住没? 用chrome浏览器打开页面 → 按f12打开开发者工具 → 打开Sources → 打开你要调试的js代码文件 → 在行号上单击一下,OK!恭喜你的处女断点打上了,哈哈~~ 2.断点怎么打才合适? 打断点操作很简单,核心的