按照栈生长方向分:可以分为递增栈(向高地址生长);递减栈(向低地址生长)
按照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, r43edc: e59f001c ldr r0, [pc, #28] ; 3f00 <f3+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 <printk>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, #103eec: 1afffff9 bne 3ed8 <f3+0x8>{printk("%d\n", i);}*addr = 0x123;3ef0: e3a03000 mov r3, #03ef4: e3002123 movw r2, #291 ; 0x1233ef8: e5832000 str r2, [r3]3efc: e8bd8010 pop {r4, pc}3f00: 0000034c .word 0x0000034c00003f04 <f2>:return;
}
void f2(int a, int b) {3f04: e92d4038 push {r3, r4, r5, lr}3f08: e1a04000 mov r4, r03f0c: e1a05001 mov r5, r1int d = 0;int *addr = 0;f3();3f10: ebfffffe bl 3ed0 <f3>d = a + b;printk("%d, %p\n", d, addr);3f14: e0841005 add r1, r4, r53f18: e3000000 movw r0, #03f1c: e3a02000 mov r2, #03f20: e3400000 movt r0, #0return;
}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 <printk>00003f2c <f1>:return;
}void f1(int a, int b) {3f2c: e92d4010 push {r4, lr}int c = 0;c = a + b;3f30: e0804001 add r4, r0, r1f2(c,20);3f34: e3a01014 mov r1, #203f38: e1a00004 mov r0, r43f3c: ebfffffe bl 3f04 <f2>while (c > 0)3f40: e3540000 cmp r4, #03f44: d8bd8010 pople {r4, pc}{ printk("%d\n", c);3f48: e1a01004 mov r1, r43f4c: e59f000c ldr r0, [pc, #12] ; 3f60 <f1+0x34>3f50: ebfffffe bl 0 <printk>void f1(int a, int b) {int c = 0;c = a + b;f2(c,20);while (c > 0)3f54: e2544001 subs r4, r4, #13f58: 1afffffa bne 3f48 <f1+0x1c>3f5c: e8bd8010 pop {r4, pc}3f60: 0000034c .word 0x0000034c00003f64 <test_thread>:
}
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, sp3f6c: e3c23d7f bic r3, r2, #8128 ; 0x1fc0unsigned 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 ; 0xff003f74: e3c3303f bic r3, r3, #63 ; 0x3f3f78: e340101f movt r1, #313f7c: e3000000 movw r0, #03f80: e3400000 movt r0, #03f84: e5932004 ldr r2, [r3, #4]3f88: e5933014 ldr r3, [r3, #20]3f8c: e0021001 and r1, r2, r13f90: e2022cff and r2, r2, #65280 ; 0xff003f94: ebfffffe bl 0 <printk>/*local_irq_disable();while (1){}*/f1(10, 20);3f98: e3a0000a mov r0, #103f9c: e3a01014 mov r1, #203fa0: ebfffffe bl 3f2c <f1>return 0;
}3fa4: e3a00000 mov r0, #03fa8: 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 ARMEntering kdb (current=0xeea2e880, pid 649) on processor 3 Oops: (null) due to oops @ 0xc03087a8dCPU: 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 : [<c03087a8>] lr : [<c0308798>] 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 [<c0014320>] (unwind_backtrace) from [<c0010fa4>] (show_stack+0x10/0x14) [<c0010fa4>] (show_stack) from [<c045cc6c>] (dump_stack+0x74/0x90) [<c045cc6c>] (dump_stack) from [<c00823f4>] (kdb_dumpregs+0x30/0x50) [<c00823f4>] (kdb_dumpregs) from [<c0084754>] (kdb_main_loop+0x31c/0x70c) [<c0084754>] (kdb_main_loop) from [<c0087128>] (kdb_stub+0x1e0/0x44c) [<c0087128>] (kdb_stub) from [<c007dbac>] (kgdb_cpu_enter+0x3c4/0x6e0) [<c007dbac>] (kgdb_cpu_enter) from [<c007e150>] (kgdb_handle_exception+0x168/0x1d0) [<c007e150>] (kgdb_handle_exception) from [<c0013998>] (kgdb_notify+0x24/0x3c) [<c0013998>] (kgdb_notify) from [<c003f95c>] (notifier_call_chain+0x44/0x84) [<c003f95c>] (notifier_call_chain) from [<c003f9b4>] (__atomic_notifier_call_chain+0x18/0x20) [<c003f9b4>] (__atomic_notifier_call_chain) from [<c003f9d4>] (atomic_notifier_call_chain+0x18/0x20) [<c003f9d4>] (atomic_notifier_call_chain) from [<c0040108>] (notify_die+0x3c/0x44) [<c0040108>] (notify_die) from [<c0011090>] (die+0xe8/0x2c8) [<c0011090>] (die) from [<c0459e8c>] (__do_kernel_fault.part.8+0x54/0x74) [<c0459e8c>] (__do_kernel_fault.part.8) from [<c0019a28>] (do_page_fault+0x1a8/0x3a4) [<c0019a28>] (do_page_fault) from [<c00084dc>] (do_DataAbort+0x34/0x98) [<c00084dc>] (do_DataAbort) from [<c0011a18>] (__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 [<c0011a18>] (__dabt_svc) from [<c03087a8>] (f3+0x28/0x34) [<c03087a8>] (f3) from [<c03087c4>] (f2+0x10/0x28) [<c03087c4>] (f2) from [<c03087f0>] (f1+0x14/0x38) [<c03087f0>] (f1) from [<c0308854>] (test_thread+0x40/0x48) [<c0308854>] (test_thread) from [<c003c4fc>] (kthread+0xcc/0xe8) [<c003c4fc>] (kthread) from [<c000e4f8>] (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里面的一样
[<c03087a8>] (f3) from [<c03087c4>] (f2+0x10/0x28)
[<c03087c4>] (f2) from [<c03087f0>] (f1+0x14/0x38)
[<c03087f0>] (f1) from [<c0308854>] (test_thread+0x40/0x48)
[<c0308854>] (test_thread) from [<c003c4fc>] (kthread+0xcc/0xe8)
[<c003c4fc>] (kthread) from [<c000e4f8>] (ret_from_fork+0x14/0x3c)
另外也可以用命令mds直接查看