按照栈生长方向分:可以分为递增栈(向高地址生长);递减栈(向低地址生长)
按照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
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
{
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
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
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直接查看