按照栈生长方向分:可以分为递增栈(向高地址生长);递减栈(向低地址生长)
按照sp执行位置来分:满栈(sp指向栈顶元素的位置);空栈(sp指向即将入栈的元素位置)
我这个环境是满减栈
其实通过函数栈推导函数调用过程主要就是结合sp的位置以及汇编代码的压栈信息。找到LR寄存器的位置。
代码示例
起了一个内核线程,在函数f3里面会访问空指针,然后进入kdb
- void f3(void ) {
- int i = 0;
- int* addr = NULL;
- for (i = 0; i < 10; ++i)
- {
- printk("%d\n", i);
- }
- *addr = 0x123;
- return;
- }
- void f2(int a, int b) {
- int d = 0;
- int *addr = 0;
- f3();
- d = a + b;
- printk("%d, %p\n", d, addr);
- return;
- }
-
- void f1(int a, int b) {
- int c = 0;
- c = a + b;
- f2(c,20);
- while (c > 0)
- {
- printk("%d\n", c);
- --c;
- }
- return;
- }
- struct timer_list timer;
- spinlock_t mylock;
- static struct task_struct *test_task;
- int test_thread(void* a)
- {
- unsigned long flags;
- printk(KERN_EMERG "\r\n softlockup simulate, in_interrupt %u in_softirq %u, cpu id %d\n", in_interrupt(), in_softirq(), smp_processor_id());
-
- /*local_irq_disable();
- while (1){}*/
- f1(10, 20);
- return 0;
- }
对应的汇编代码如下
- void f3(void ) {
- 3ed0: e92d4010 push {r4, lr}
- int i = 0;
- int* addr = NULL;
- for (i = 0; i < 10; ++i)
- 3ed4: e3a04000 mov r4, #0
- {
- printk("%d\n", i);
- 3ed8: e1a01004 mov r1, r4
- 3edc: e59f001c ldr r0, [pc, #28] ; 3f00
0x30> - dev->dev_addr[5] = (u8)(mac_high16 >> 8);
- }
- void f3(void ) {
- int i = 0;
- int* addr = NULL;
- for (i = 0; i < 10; ++i)
- 3ee0: e2844001 add r4, r4, #1
- {
- printk("%d\n", i);
- 3ee4: ebfffffe bl 0
- dev->dev_addr[5] = (u8)(mac_high16 >> 8);
- }
- void f3(void ) {
- int i = 0;
- int* addr = NULL;
- for (i = 0; i < 10; ++i)
- 3ee8: e354000a cmp r4, #10
- 3eec: 1afffff9 bne 3ed8
0x8> - {
- printk("%d\n", i);
- }
- *addr = 0x123;
- 3ef0: e3a03000 mov r3, #0
- 3ef4: e3002123 movw r2, #291 ; 0x123
- 3ef8: e5832000 str r2, [r3]
- 3efc: e8bd8010 pop {r4, pc}
- 3f00: 0000034c .word 0x0000034c
-
- 00003f04
: - return;
- }
- void f2(int a, int b) {
- 3f04: e92d4038 push {r3, r4, r5, lr}
- 3f08: e1a04000 mov r4, r0
- 3f0c: e1a05001 mov r5, r1
- int d = 0;
- int *addr = 0;
- f3();
- 3f10: ebfffffe bl 3ed0
- d = a + b;
- printk("%d, %p\n", d, addr);
- 3f14: e0841005 add r1, r4, r5
- 3f18: e3000000 movw r0, #0
- 3f1c: e3a02000 mov r2, #0
- 3f20: e3400000 movt r0, #0
- return;
- }
- 3f24: e8bd4038 pop {r3, r4, r5, lr}
- void f2(int a, int b) {
- int d = 0;
- int *addr = 0;
- f3();
- d = a + b;
- printk("%d, %p\n", d, addr);
- 3f28: eafffffe b 0
-
- 00003f2c
: - return;
- }
-
- void f1(int a, int b) {
- 3f2c: e92d4010 push {r4, lr}
- int c = 0;
- c = a + b;
- 3f30: e0804001 add r4, r0, r1
- f2(c,20);
- 3f34: e3a01014 mov r1, #20
- 3f38: e1a00004 mov r0, r4
- 3f3c: ebfffffe bl 3f04
- while (c > 0)
- 3f40: e3540000 cmp r4, #0
- 3f44: d8bd8010 pople {r4, pc}
- {
- printk("%d\n", c);
- 3f48: e1a01004 mov r1, r4
- 3f4c: e59f000c ldr r0, [pc, #12] ; 3f60
0x34> - 3f50: ebfffffe bl 0
-
- void f1(int a, int b) {
- int c = 0;
- c = a + b;
- f2(c,20);
- while (c > 0)
- 3f54: e2544001 subs r4, r4, #1
- 3f58: 1afffffa bne 3f48
0x1c> - 3f5c: e8bd8010 pop {r4, pc}
- 3f60: 0000034c .word 0x0000034c
-
- 00003f64
: - }
- struct timer_list timer;
- spinlock_t mylock;
- static struct task_struct *test_task;
- int test_thread(void* a)
- {
- 3f64: e92d4008 push {r3, lr}
- 3f68: e1a0200d mov r2, sp
- 3f6c: e3c23d7f bic r3, r2, #8128 ; 0x1fc0
- unsigned long flags;
- printk(KERN_EMERG "\r\n softlockup simulate, in_interrupt %u in_softirq %u, cpu id %d\n", in_interrupt(), in_softirq(), smp_processor_id());
- 3f70: e3a01cff mov r1, #65280 ; 0xff00
- 3f74: e3c3303f bic r3, r3, #63 ; 0x3f
- 3f78: e340101f movt r1, #31
- 3f7c: e3000000 movw r0, #0
- 3f80: e3400000 movt r0, #0
- 3f84: e5932004 ldr r2, [r3, #4]
- 3f88: e5933014 ldr r3, [r3, #20]
- 3f8c: e0021001 and r1, r2, r1
- 3f90: e2022cff and r2, r2, #65280 ; 0xff00
- 3f94: ebfffffe bl 0
-
- /*local_irq_disable();
- while (1){}*/
- f1(10, 20);
- 3f98: e3a0000a mov r0, #10
- 3f9c: e3a01014 mov r1, #20
- 3fa0: ebfffffe bl 3f2c
- return 0;
- }
- 3fa4: e3a00000 mov r0, #0
- 3fa8: e8bd8008 pop {r3, pc}
从上面的汇编代码可以看到每个函数的入栈信息如下。假设函数f3里面的栈顶指针为 sp_f3
完整的异常log如下:
Unable to handle kernel NULL pointer dereference at virtual address 00000000 pgd = c0004000 [00000000] *pgd=00000000 Internal error: Oops: 817 [#1] SMP ARM Entering kdb (current=0xeea2e880, pid 649) on processor 3 Oops: (null) due to oops @ 0xc03087a8 dCPU: 3 PID: 649 Comm: test_task Not tainted 3.16.0 #65 dtask: eea2e880 ti: ee226000 task.ti: ee226000 PC is at f3+0x28/0x34 LR is at f3+0x18/0x34 pc : [] lr : [ ] psr: 60000013 sp : ee227f40 ip : 00000001 fp : 00000000 r10: 00000000 r9 : 00000000 r8 : 00000000 r7 : c0308814 r6 : 00000000 r5 : 00000014 r4 : 0000000a r3 : 00000000 r2 : 00000123 r1 : 20000093 r0 : 00000001 Flags: nZCv IRQs on FIQs on Mode SVC_32 ISA ARM Segment kernel Control: 10c53c7d Table: 8e30006a DAC: 00000015 dCPU: 3 PID: 649 Comm: test_task Not tainted 3.16.0 #65 [] (unwind_backtrace) from [ ] (show_stack+0x10/0x14) [] (show_stack) from [ ] (dump_stack+0x74/0x90) [] (dump_stack) from [ ] (kdb_dumpregs+0x30/0x50) [] (kdb_dumpregs) from [ ] (kdb_main_loop+0x31c/0x70c) [] (kdb_main_loop) from [ ] (kdb_stub+0x1e0/0x44c) [] (kdb_stub) from [ ] (kgdb_cpu_enter+0x3c4/0x6e0) [] (kgdb_cpu_enter) from [ ] (kgdb_handle_exception+0x168/0x1d0) [] (kgdb_handle_exception) from [ ] (kgdb_notify+0x24/0x3c) [] (kgdb_notify) from [ ] (notifier_call_chain+0x44/0x84) [] (notifier_call_chain) from [ ] (__atomic_notifier_call_chain+0x18/0x20) [] (__atomic_notifier_call_chain) from [ ] (atomic_notifier_call_chain+0x18/0x20) [] (atomic_notifier_call_chain) from [ ] (notify_die+0x3c/0x44) [] (notify_die) from [ ] (die+0xe8/0x2c8) [] (die) from [ ] (__do_kernel_fault.part.8+0x54/0x74) [] (__do_kernel_fault.part.8) from [ ] (do_page_fault+0x1a8/0x3a4) [] (do_page_fault) from [ ] (do_DataAbort+0x34/0x98) [] (do_DataAbort) from [ ] (__dabt_svc+0x38/0x60) Exception stack(0xee227ef8 to 0xee227f40) 7ee0: 00000001 20000093 7f00: 00000123 00000000 0000000a 00000014 00000000 c0308814 00000000 00000000 7f20: 00000000 00000000 00000001 ee227f40 c0308798 c03087a8 60000013 ffffffff [] (__dabt_svc) from [ ] (f3+0x28/0x34) [] (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)
从异常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直接查看