arm栈推导

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

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

arm栈推导_第1张图片

我这个环境是满减栈

 其实通过函数栈推导函数调用过程主要就是结合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

arm栈推导_第2张图片

完整的异常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指向的地址里面的内容

arm栈推导_第3张图片

同理一级一级往上推

 arm栈推导_第4张图片

 arm栈推导_第5张图片

 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直接查看 

arm栈推导_第6张图片

你可能感兴趣的:(开发语言)