欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【ARM裸板】启动文件与栈的分析

程序员文章站 2022-06-08 22:05:51
...

1.start.S过程

  • 设置栈
  • 调用main函数,并把返回地址保存在LR(R14)中
.text
.global _start
_start:
	/* 设置内存: sp栈 */
	ldr sp, = 4096 /* nand 启动 */
	/* 调用main函数 */ 
	bl main 
halt:
	b halt

2.led.c过程

  • 定义两个局部变量
  • 设置变量
  • return 0
int main(void)
{
	volatile unsigned int *pGPFCON = (volatile unsigned int *)0x56000050;
	volatile unsigned int *pGPFDAT = (volatile unsigned int *)0x56000054;
	
	*pGPFCON = 0x100;
	*pGPFDAT = 0;
	return 0;
}

3.问题

3.1 为什么要设置栈?

  • 因为C函数所需
    • 保存局部变量
    • 保存LR等寄存器(返回地址)

【ARM裸板】启动文件与栈的分析
调用者如何传递参数给被调用者
被调用者如何传返回这给调用者
怎么从栈中恢复那些寄存器

  • 调用者和被调用者通过r0-r3寄存器传递参数和返回值
  • 在函数中,r4-r11可能被使用,所以:在入口保存他们,在出口恢复他们
  • 高标号寄存器存放在高地址

3.2 反汇编程序分析

00000000 <_start>:
   0:	e3a0da01 	mov	sp, #4096	; 0x1000            		;设置栈指针大小为4096
   4:	eb000000 	bl	c <main>								;跳转到[0x0c]main函数,并保存返回地址(下一条指令的地址,0x08)到LR中

00000008 <halt>:
   8:	eafffffe 	b	8 <halt>

0000000c <main>:
   c:	e52db004 	push	{fp}		; (str fp, [sp, #-4]!)  ;fp的值存入[4096-4](4092),sp = sp-4 ==> 将【栈帧底部】指针fp压入栈中;创建属于main函数的栈帧。  
  10:	e28db000 	add	fp, sp, #0								;fp = sp+0 = 4092+0 = 4092 ==> fp指针为函数栈帧的底部
  14:	e24dd00c 	sub	sp, sp, #12								;sp = 4092-12 = 4080 ==> sp指针为【栈帧顶部】,同时为栈的栈顶
  18:	e59f3034 	ldr	r3, [pc, #52]	; 54 <main+0x48>		;r3 = [0x18+8+52] = [0x54] 
  1c:	e50b3008 	str	r3, [fp, #-8]							;r3(0x56000050)存放在[4084] ==> 保存局部变量
  20:	e59f3030 	ldr	r3, [pc, #48]	; 58 <main+0x4c>		;r3 = [0x20+8+48] = [0x58]
  24:	e50b300c 	str	r3, [fp, #-12]							;r3(0x56000054)存放在[4080] ==> 保存局部变量
  28:	e51b3008 	ldr	r3, [fp, #-8]							;r3 = [0x4084] = 0x56000050 
  2c:	e3a02c01 	mov	r2, #256	; 0x100						;r2 = 0x100
  30:	e5832000 	str	r2, [r3]								;[r3] = 0x100 ==> 写入0x100至GPFCON寄存器
  34:	e51b300c 	ldr	r3, [fp, #-12]							;r3 = [0x4080] = 0x56000054
  38:	e3a02000 	mov	r2, #0									;r2 = 0
  3c:	e5832000 	str	r2, [r3]								;[r3] = 0 ==> 写入0至GPFDAT寄存器
  40:	e3a03000 	mov	r3, #0									;r3 = 0 ==> return 0的返回值存放先在r3
  44:	e1a00003 	mov	r0, r3									;r0 = 0 ==> return 0的返回值存放在r0
  48:	e28bd000 	add	sp, fp, #0								;sp = fp-0 = 4092 
  4c:	e49db004 	pop	{fp}		; (ldr fp, [sp], #4)		;fp = [4092] ==> 恢复fp,sp = sp+4 = 4096 ==>恢复栈
  50:	e12fff1e 	bx	lr										;跳转值lr寄存器所指向的地址并切换指令集
  54:	56000050 			; <UNDEFINED> instruction: 0x56000050
  58:	56000054 			; <UNDEFINED> instruction: 0x56000054
  • 上面的汇编代码可以看到,并没有想上面图中所画的,将fp, sp, lr, pc全部都入栈,而是只入栈这四个寄存器中有改动的。fp是肯定要保存的,它指向的是每个函数栈帧的栈基址,而sp一般不用入栈,因为它的值一般保存在fp中,因为刚进入一个函数的时候,将上个函数的fp入栈保存以后,当前函数的栈空间应该是空的,fp应该指向与sp相同的位置,然后才会对sp做减法来分配栈空间保存临时变量。而如果当前函数中没有对其它函数的调用的时候,是不会对lr寄存器做修改的,所以也就不用保存了。
相关标签: 【ARM裸板】