arm栈推导

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

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

我这个环境是满减栈

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

代码示例

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

cpp 复制代码
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;
}

对应的汇编代码如下

cpp 复制代码
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 <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, #10
    3eec:	1afffff9 	bne	3ed8 <f3+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 <f2>:
  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 <f3>
  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 <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, r1
  f2(c,20);
    3f34:	e3a01014 	mov	r1, #20
    3f38:	e1a00004 	mov	r0, r4
    3f3c:	ebfffffe 	bl	3f04 <f2>
  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 <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, #1
    3f58:	1afffffa 	bne	3f48 <f1+0x1c>
    3f5c:	e8bd8010 	pop	{r4, pc}
    3f60:	0000034c 	.word	0x0000034c

00003f64 <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, 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 <printk>
	
	/*local_irq_disable();
	while (1){}*/
	f1(10, 20);
    3f98:	e3a0000a 	mov	r0, #10
    3f9c:	e3a01014 	mov	r1, #20
    3fa0:	ebfffffe 	bl	3f2c <f1>
	return 0;
}
    3fa4:	e3a00000 	mov	r0, #0
    3fa8:	e8bd8008 	pop	{r3, pc}

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

完整的异常log如下:

cpp 复制代码
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 : [<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直接查看

相关推荐
测试盐8 分钟前
c++编译过程初识
开发语言·c++
赖赖赖先生14 分钟前
fastadmin 框架 生成qr code 二维码图片,PHP 7.4版本
开发语言·php
玉红7771 小时前
R语言的数据类型
开发语言·后端·golang
夜斗(dou)1 小时前
node.js文件压缩包解析,反馈解析进度,解析后的文件字节正常
开发语言·javascript·node.js
觅远1 小时前
python+PyMuPDF库:(一)创建pdf文件及内容读取和写入
开发语言·python·pdf
神雕杨2 小时前
node js 过滤空白行
开发语言·前端·javascript
lvbu_2024war012 小时前
MATLAB语言的网络编程
开发语言·后端·golang
single5942 小时前
【c++笔试强训】(第四十五篇)
java·开发语言·数据结构·c++·算法
游客5202 小时前
自动化办公-合并多个excel
开发语言·python·自动化·excel
Cshaosun3 小时前
js版本之ES6特性简述【Proxy、Reflect、Iterator、Generator】(五)
开发语言·javascript·es6