• Linux应用调试之配置修改内核打印用户态段错误信息


    目录

    1 驱动调试回顾

    2 修改内核打印用户态段错误信息

     3 编译要调试的应用程序

    4 执行有问题的应用程序、调试


    1 驱动调试回顾

    前面驱动程序调试那里,我们在init函数里面故意没有对gpfcon寄存器地址进行ioremap,然后再open函数中给gpfcon寄存器赋值的时候就出现了段错误,然后报错的时候会打印一堆错误信息,我们根据出错时的PC值就可以找到出错的位置。那么我们可以让应用程序报错的时候也打印这些错误信息,这需要我们增加一些代码,我们驱动报错时打印的信息如下

    2 修改内核打印用户态段错误信息

    那么我们在内核源码中搜索Unable to handle kernel

    grep "Unable to handle kernel" * -nR

     可以看到在arch下面的某种架构下面出现了很多东西,那我们进入到arch/arm目录下继续搜索,得到

    1. /*
    2. * Oops. The kernel tried to access some page that wasn't present.
    3. */
    4. static void
    5. __do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr,
    6. struct pt_regs *regs)
    7. {
    8. /*
    9. * Are we prepared to handle this kernel fault?
    10. */
    11. if (fixup_exception(regs))
    12. return;
    13. /*
    14. * No handler, we'll have to terminate things with extreme prejudice.
    15. */
    16. bust_spinlocks(1);
    17. printk(KERN_ALERT
    18. "Unable to handle kernel %s at virtual address %08lx\n",
    19. (addr < PAGE_SIZE) ? "NULL pointer dereference" :
    20. "paging request", addr);
    21. show_pte(mm, addr);
    22. die("Oops", regs, fsr);
    23. bust_spinlocks(0);
    24. do_exit(SIGKILL);
    25. }

    然后我们还能看到

    1. void do_bad_area(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
    2. {
    3. struct task_struct *tsk = current;
    4. struct mm_struct *mm = tsk->active_mm;
    5. /*
    6. * If we are in kernel mode at this point, we
    7. * have no context to handle this fault with.
    8. */
    9. if (user_mode(regs))
    10. __do_user_fault(tsk, addr, fsr, SIGSEGV, SEGV_MAPERR, regs);
    11. else
    12. __do_kernel_fault(mm, addr, fsr, regs);
    13. }

    应用程序执行的时候,有可能在用户态,有可能在内核态,在用户态的时候出错用__do_user_fault打印,在内核态出错就用__do_kernel_fault。然后我们看一下__do_user_fault代码(arch/arm/mm/fault.c)

    1. /*
    2. * Something tried to access memory that isn't in our memory map..
    3. * User mode accesses just cause a SIGSEGV
    4. */
    5. static void
    6. __do_user_fault(struct task_struct *tsk, unsigned long addr,
    7. unsigned int fsr, unsigned int sig, int code,
    8. struct pt_regs *regs)
    9. {
    10. struct siginfo si;
    11. #ifdef CONFIG_DEBUG_USER
    12. if (user_debug & UDBG_SEGV) {
    13. printk(KERN_DEBUG "%s: unhandled page fault (%d) at 0x%08lx, code 0x%03x\n",
    14. tsk->comm, sig, addr, fsr);
    15. show_pte(tsk->mm, addr);
    16. show_regs(regs);
    17. }
    18. #endif
    19. tsk->thread.address = addr;
    20. tsk->thread.error_code = fsr;
    21. tsk->thread.trap_no = 14;
    22. si.si_signo = sig;
    23. si.si_errno = 0;
    24. si.si_code = code;
    25. si.si_addr = (void __user *)addr;
    26. force_sig_info(sig, &si, tsk);
    27. }

    如果我们想让打印能够打印出来,那需要满足两个条件

    • #ifdef CONFIG_DEBUG_USER,通过配置内核满足该条件
    • if (user_debug & UDBG_SEGV) {, 通过uboot命令行参数满足该条件

    条件if (user_debug & UDBG_SEGV),其中#define UDBG_SEGV   (1 << 3),变量user_debug在

    1. #ifdef CONFIG_DEBUG_USER
    2. unsigned int user_debug;
    3. static int __init user_debug_setup(char *str)
    4. {
    5. get_option(&str, &user_debug);
    6. return 1;
    7. }
    8. __setup("user_debug=", user_debug_setup);
    9. #endif

    这里__setup("user_debug=", user_debug_setup);是指BootLoader启动的时候可以传入这些参数,

    set bootargs console=ttySAC0 root=/dev/nfs nfsroot=192.168.1.123:/work/nfs_root/first_fs ip=192.168.1.17 ipaddr=192.168.1.17 user_debug=0xff

     3 编译要调试的应用程序

    1. #include
    2. void C(int *p)
    3. {
    4. *p = 0x12;
    5. }
    6. void B(int *p)
    7. {
    8. C(p);
    9. }
    10. void A(int *p)
    11. {
    12. B(p);
    13. }
    14. void A2(int *p)
    15. {
    16. C(p);
    17. }
    18. int main(int argc, char **argv)
    19. {
    20. int a;
    21. int *p = NULL;
    22. A2(&a); // A2 > C
    23. printf("a = 0x%x\n", a);
    24. A(p); // A > B > C
    25. return 0;
    26. }

    4 执行有问题的应用程序、调试

    运行应用程序后,打印如下信息

    1. ./test_debug
    2. a = 0x12
    3. pgd = c04c8000
    4. [00000000] *pgd=33d08031, *pte=00000000, *ppte=00000000
    5. Pid: 772, comm: test_debug
    6. CPU: 0 Not tainted (2.6.22.6 #1)
    7. PC is at 0x84ac
    8. LR is at 0x84d0
    9. pc : [<000084ac>] lr : [<000084d0>] psr: 60000010
    10. sp : bed9fe40 ip : bed9fe54 fp : bed9fe50
    11. r10: 4013365c r9 : 00000000 r8 : 00008514
    12. r7 : 00000001 r6 : 000085cc r5 : 00008568 r4 : bed9fec4
    13. r3 : 00000012 r2 : 00000000 r1 : 00001000 r0 : 00000000
    14. Flags: nZCv IRQs on FIQs on Mode USER_32 Segment user
    15. Control: c000717f Table: 304c8000 DAC: 00000015
    16. [] (show_regs+0x0/0x4c) from [] (__do_user_fault+0x5c/0xa4)
    17. r4:c04a6840
    18. [] (__do_user_fault+0x0/0xa4) from [] (do_page_fault+0x1dc/0x20c)
    19. r7:c00261e0 r6:c0024cf8 r5:c04a6840 r4:ffffffec
    20. [] (do_page_fault+0x0/0x20c) from [] (do_DataAbort+0x3c/0xa0)
    21. [] (do_DataAbort+0x0/0xa0) from [] (ret_from_exception+0x0/0x10)
    22. Exception stack(0xc3e7bfb0 to 0xc3e7bff8)
    23. bfa0: 00000000 00001000 00000000 00000012
    24. bfc0: bed9fec4 00008568 000085cc 00000001 00008514 00000000 4013365c bed9fe50
    25. bfe0: bed9fe54 bed9fe40 000084d0 000084ac 60000010 ffffffff
    26. r8:00008514 r7:00000001 r6:000085cc r5:00008568 r4:c039bfc8
    27. Segmentation fault

    然后反汇编我们的应用程序

    arm-linux-objdump -D test_debug > test_debug.dis

    然后再里面搜索出错的PC 84ac得到出错位置是在C函数中

    1. 00008490 :
    2. 8490: e1a0c00d mov ip, sp
    3. 8494: e92dd800 stmdb sp!, {fp, ip, lr, pc}
    4. 8498: e24cb004 sub fp, ip, #4 ; 0x4
    5. 849c: e24dd004 sub sp, sp, #4 ; 0x4
    6. 84a0: e50b0010 str r0, [fp, #-16]
    7. 84a4: e51b2010 ldr r2, [fp, #-16]
    8. 84a8: e3a03012 mov r3, #18 ; 0x12
    9. 84ac: e5823000 str r3, [r2] // r3=0x12, r2=0
    10. 84b0: e89da808 ldmia sp, {r3, fp, sp, pc}

    这里是把r3存到r2地址那里,r2=0,那么相当于保存到0地址,当然会报错。

    至此配置修改内核打印用户态段错误信息的介绍已经结束了。

  • 相关阅读:
    Lombok
    Spring面试题:(七)Spring AOP思想及实现
    表面富集季胺盐交联/多乙烯多胺接枝改性/甲基咪唑氯修饰聚苯乙烯微球的研究和制备
    多肽Caerulein (desulfated)、pGlu-QDYTGWMDF-NH2、20994-83-6
    2022.11.17 HDU-4911 Inversion
    三辊闸机的应用领域和特点
    AdaBoost(上):数据分析 | 数据挖掘 | 十大算法之一
    【毕业设计】17-基于单片机的矿井提升机_步进电机控制装置设计(原理图+仿真+源代码+实物图+答辩论文+答辩PPT)
    Java 常用API
    zipkin2.24.2源码install遇见的问题
  • 原文地址:https://blog.csdn.net/u013171226/article/details/126424162