• arm栈推导


    按照栈生长方向分:可以分为递增栈(向高地址生长);递减栈(向低地址生长)

    按照sp执行位置来分:满栈(sp指向栈顶元素的位置);空栈(sp指向即将入栈的元素位置)

    我这个环境是满减栈

     其实通过函数栈推导函数调用过程主要就是结合sp的位置以及汇编代码的压栈信息。找到LR寄存器的位置。

    代码示例

    起了一个内核线程,在函数f3里面会访问空指针,然后进入kdb

    1. void f3(void ) {
    2. int i = 0;
    3. int* addr = NULL;
    4. for (i = 0; i < 10; ++i)
    5. {
    6. printk("%d\n", i);
    7. }
    8. *addr = 0x123;
    9. return;
    10. }
    11. void f2(int a, int b) {
    12. int d = 0;
    13. int *addr = 0;
    14. f3();
    15. d = a + b;
    16. printk("%d, %p\n", d, addr);
    17. return;
    18. }
    19. void f1(int a, int b) {
    20. int c = 0;
    21. c = a + b;
    22. f2(c,20);
    23. while (c > 0)
    24. {
    25. printk("%d\n", c);
    26. --c;
    27. }
    28. return;
    29. }
    30. struct timer_list timer;
    31. spinlock_t mylock;
    32. static struct task_struct *test_task;
    33. int test_thread(void* a)
    34. {
    35. unsigned long flags;
    36. printk(KERN_EMERG "\r\n softlockup simulate, in_interrupt %u in_softirq %u, cpu id %d\n", in_interrupt(), in_softirq(), smp_processor_id());
    37. /*local_irq_disable();
    38. while (1){}*/
    39. f1(10, 20);
    40. return 0;
    41. }

    对应的汇编代码如下 

    1. void f3(void ) {
    2. 3ed0: e92d4010 push {r4, lr}
    3. int i = 0;
    4. int* addr = NULL;
    5. for (i = 0; i < 10; ++i)
    6. 3ed4: e3a04000 mov r4, #0
    7. {
    8. printk("%d\n", i);
    9. 3ed8: e1a01004 mov r1, r4
    10. 3edc: e59f001c ldr r0, [pc, #28] ; 3f00 0x30>
    11. dev->dev_addr[5] = (u8)(mac_high16 >> 8);
    12. }
    13. void f3(void ) {
    14. int i = 0;
    15. int* addr = NULL;
    16. for (i = 0; i < 10; ++i)
    17. 3ee0: e2844001 add r4, r4, #1
    18. {
    19. printk("%d\n", i);
    20. 3ee4: ebfffffe bl 0
    21. dev->dev_addr[5] = (u8)(mac_high16 >> 8);
    22. }
    23. void f3(void ) {
    24. int i = 0;
    25. int* addr = NULL;
    26. for (i = 0; i < 10; ++i)
    27. 3ee8: e354000a cmp r4, #10
    28. 3eec: 1afffff9 bne 3ed8 0x8>
    29. {
    30. printk("%d\n", i);
    31. }
    32. *addr = 0x123;
    33. 3ef0: e3a03000 mov r3, #0
    34. 3ef4: e3002123 movw r2, #291 ; 0x123
    35. 3ef8: e5832000 str r2, [r3]
    36. 3efc: e8bd8010 pop {r4, pc}
    37. 3f00: 0000034c .word 0x0000034c
    38. 00003f04 :
    39. return;
    40. }
    41. void f2(int a, int b) {
    42. 3f04: e92d4038 push {r3, r4, r5, lr}
    43. 3f08: e1a04000 mov r4, r0
    44. 3f0c: e1a05001 mov r5, r1
    45. int d = 0;
    46. int *addr = 0;
    47. f3();
    48. 3f10: ebfffffe bl 3ed0
    49. d = a + b;
    50. printk("%d, %p\n", d, addr);
    51. 3f14: e0841005 add r1, r4, r5
    52. 3f18: e3000000 movw r0, #0
    53. 3f1c: e3a02000 mov r2, #0
    54. 3f20: e3400000 movt r0, #0
    55. return;
    56. }
    57. 3f24: e8bd4038 pop {r3, r4, r5, lr}
    58. void f2(int a, int b) {
    59. int d = 0;
    60. int *addr = 0;
    61. f3();
    62. d = a + b;
    63. printk("%d, %p\n", d, addr);
    64. 3f28: eafffffe b 0
    65. 00003f2c :
    66. return;
    67. }
    68. void f1(int a, int b) {
    69. 3f2c: e92d4010 push {r4, lr}
    70. int c = 0;
    71. c = a + b;
    72. 3f30: e0804001 add r4, r0, r1
    73. f2(c,20);
    74. 3f34: e3a01014 mov r1, #20
    75. 3f38: e1a00004 mov r0, r4
    76. 3f3c: ebfffffe bl 3f04
    77. while (c > 0)
    78. 3f40: e3540000 cmp r4, #0
    79. 3f44: d8bd8010 pople {r4, pc}
    80. {
    81. printk("%d\n", c);
    82. 3f48: e1a01004 mov r1, r4
    83. 3f4c: e59f000c ldr r0, [pc, #12] ; 3f60 0x34>
    84. 3f50: ebfffffe bl 0
    85. void f1(int a, int b) {
    86. int c = 0;
    87. c = a + b;
    88. f2(c,20);
    89. while (c > 0)
    90. 3f54: e2544001 subs r4, r4, #1
    91. 3f58: 1afffffa bne 3f48 0x1c>
    92. 3f5c: e8bd8010 pop {r4, pc}
    93. 3f60: 0000034c .word 0x0000034c
    94. 00003f64 :
    95. }
    96. struct timer_list timer;
    97. spinlock_t mylock;
    98. static struct task_struct *test_task;
    99. int test_thread(void* a)
    100. {
    101. 3f64: e92d4008 push {r3, lr}
    102. 3f68: e1a0200d mov r2, sp
    103. 3f6c: e3c23d7f bic r3, r2, #8128 ; 0x1fc0
    104. unsigned long flags;
    105. printk(KERN_EMERG "\r\n softlockup simulate, in_interrupt %u in_softirq %u, cpu id %d\n", in_interrupt(), in_softirq(), smp_processor_id());
    106. 3f70: e3a01cff mov r1, #65280 ; 0xff00
    107. 3f74: e3c3303f bic r3, r3, #63 ; 0x3f
    108. 3f78: e340101f movt r1, #31
    109. 3f7c: e3000000 movw r0, #0
    110. 3f80: e3400000 movt r0, #0
    111. 3f84: e5932004 ldr r2, [r3, #4]
    112. 3f88: e5933014 ldr r3, [r3, #20]
    113. 3f8c: e0021001 and r1, r2, r1
    114. 3f90: e2022cff and r2, r2, #65280 ; 0xff00
    115. 3f94: ebfffffe bl 0
    116. /*local_irq_disable();
    117. while (1){}*/
    118. f1(10, 20);
    119. 3f98: e3a0000a mov r0, #10
    120. 3f9c: e3a01014 mov r1, #20
    121. 3fa0: ebfffffe bl 3f2c
    122. return 0;
    123. }
    124. 3fa4: e3a00000 mov r0, #0
    125. 3fa8: e8bd8008 pop {r3, pc}

    从上面的汇编代码可以看到每个函数的入栈信息如下。假设函数f3里面的栈顶指针为 sp_f3

    完整的异常log如下: 

    1. Unable to handle kernel NULL pointer dereference at virtual address 00000000
    2. pgd = c0004000
    3. [00000000] *pgd=00000000
    4. Internal error: Oops: 817 [#1] SMP ARM
    5. Entering kdb (current=0xeea2e880, pid 649) on processor 3 Oops: (null)
    6. due to oops @ 0xc03087a8
    7. dCPU: 3 PID: 649 Comm: test_task Not tainted 3.16.0 #65
    8. dtask: eea2e880 ti: ee226000 task.ti: ee226000
    9. PC is at f3+0x28/0x34
    10. LR is at f3+0x18/0x34
    11. pc : [] lr : [] psr: 60000013
    12. sp : ee227f40 ip : 00000001 fp : 00000000
    13. r10: 00000000 r9 : 00000000 r8 : 00000000
    14. r7 : c0308814 r6 : 00000000 r5 : 00000014 r4 : 0000000a
    15. r3 : 00000000 r2 : 00000123 r1 : 20000093 r0 : 00000001
    16. Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel
    17. Control: 10c53c7d Table: 8e30006a DAC: 00000015
    18. dCPU: 3 PID: 649 Comm: test_task Not tainted 3.16.0 #65
    19. [] (unwind_backtrace) from [] (show_stack+0x10/0x14)
    20. [] (show_stack) from [] (dump_stack+0x74/0x90)
    21. [] (dump_stack) from [] (kdb_dumpregs+0x30/0x50)
    22. [] (kdb_dumpregs) from [] (kdb_main_loop+0x31c/0x70c)
    23. [] (kdb_main_loop) from [] (kdb_stub+0x1e0/0x44c)
    24. [] (kdb_stub) from [] (kgdb_cpu_enter+0x3c4/0x6e0)
    25. [] (kgdb_cpu_enter) from [] (kgdb_handle_exception+0x168/0x1d0)
    26. [] (kgdb_handle_exception) from [] (kgdb_notify+0x24/0x3c)
    27. [] (kgdb_notify) from [] (notifier_call_chain+0x44/0x84)
    28. [] (notifier_call_chain) from [] (__atomic_notifier_call_chain+0x18/0x20)
    29. [] (__atomic_notifier_call_chain) from [] (atomic_notifier_call_chain+0x18/0x20)
    30. [] (atomic_notifier_call_chain) from [] (notify_die+0x3c/0x44)
    31. [] (notify_die) from [] (die+0xe8/0x2c8)
    32. [] (die) from [] (__do_kernel_fault.part.8+0x54/0x74)
    33. [] (__do_kernel_fault.part.8) from [] (do_page_fault+0x1a8/0x3a4)
    34. [] (do_page_fault) from [] (do_DataAbort+0x34/0x98)
    35. [] (do_DataAbort) from [] (__dabt_svc+0x38/0x60)
    36. Exception stack(0xee227ef8 to 0xee227f40)
    37. 7ee0: 00000001 20000093
    38. 7f00: 00000123 00000000 0000000a 00000014 00000000 c0308814 00000000 00000000
    39. 7f20: 00000000 00000000 00000001 ee227f40 c0308798 c03087a8 60000013 ffffffff
    40. [] (__dabt_svc) from [] (f3+0x28/0x34)
    41. [] (f3) from [] (f2+0x10/0x28)
    42. [] (f2) from [] (f1+0x14/0x38)
    43. [] (f1) from [] (test_thread+0x40/0x48)
    44. [] (test_thread) from [] (kthread+0xcc/0xe8)
    45. [] (kthread) from [] (ret_from_fork+0x14/0x3c)

     

    从异常log我们可以知道f3的栈顶指针为 sp : ee227f40

    f2的返回地址为ee227f40 + 4指向的地址里面的内容

    同理一级一级往上推

     

     

     c03087c4 = 0xc03087c4 (f2+0x10)

    c03087f0 = 0xc03087f0 (f1+0x14)
    c0308854 = 0xc0308854 (test_thread+0x40)
    c003c4fc = 0xc003c4fc (kthread+0xcc)

    和异常log里面的一样
    [] (f3) from [] (f2+0x10/0x28)
    [] (f2) from [] (f1+0x14/0x38)
    [] (f1) from [] (test_thread+0x40/0x48)
    [] (test_thread) from [] (kthread+0xcc/0xe8)
    [] (kthread) from [] (ret_from_fork+0x14/0x3c)

    另外也可以用命令mds直接查看 

  • 相关阅读:
    自动化测试开发 —— 如何封装自动化测试框架?
    诗诺克科技引领数字资产智能交易革命
    Vue3:用vite创建Vue3项目
    万字总结随机森林原理、核心参数以及调优思路
    第10章Tcl脚本编程(一)
    MySQL 流程控制 的详细讲解
    UE4 关卡蓝图实现开关门
    excel 日期与时间戳的相互转换
    Nginx 无法正确加载静态文件,静态文件加载404或者为html;Nginx 配置访问指定url路径项目部署;
    视频转换gif动图 - 在线gif制作工具
  • 原文地址:https://blog.csdn.net/qq_42693685/article/details/132818903