ARM运行C程序的内部机制

在前面的S3C2440逻辑的LED实验中,我们用C语言控制了LED,在C语言前面我们还编写了一段汇编代码用来设置栈,下面我们以这个实验为例子看一下C语言的内部机制。

1.汇编代码


.text
.global _start

_start:

	/* 设置内存: sp 栈 */
	ldr sp, =4096  /* nand启动 */
//	ldr sp, =0x40000000+4096  /* nor启动 */

	/* 调用main */
	bl main

halt:
	b halt
	

2.C语言代码

int main()
{
	unsigned int *pGPFCON = (unsigned int *)0x56000050;
	unsigned int *pGPFDAT = (unsigned int *)0x56000054;

	/* 配置GPF4为输出引脚 */
	*pGPFCON = 0x100;
	
	/* 设置GPF4输出0 */
	*pGPFDAT = 0;

	return 0;
}

3.反汇编代码


led.elf:     file format elf32-littlearm

Disassembly of section .text:

00000000 <_start>:
   0:	e3a0da01 	mov	sp, #4096	; 0x1000
   4:	eb000000 	bl	c <main>

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

0000000c <main>:
   c:	e1a0c00d 	mov	ip, sp
  10:	e92dd800 	stmdb	sp!, {fp, ip, lr, pc}
  14:	e24cb004 	sub	fp, ip, #4	; 0x4
  18:	e24dd008 	sub	sp, sp, #8	; 0x8
  1c:	e3a03456 	mov	r3, #1442840576	; 0x56000000
  20:	e2833050 	add	r3, r3, #80	; 0x50
  24:	e50b3010 	str	r3, [fp, #-16]
  28:	e3a03456 	mov	r3, #1442840576	; 0x56000000
  2c:	e2833054 	add	r3, r3, #84	; 0x54
  30:	e50b3014 	str	r3, [fp, #-20]
  34:	e51b2010 	ldr	r2, [fp, #-16]
  38:	e3a03c01 	mov	r3, #256	; 0x100
  3c:	e5823000 	str	r3, [r2]
  40:	e51b2014 	ldr	r2, [fp, #-20]
  44:	e3a03000 	mov	r3, #0	; 0x0
  48:	e5823000 	str	r3, [r2]
  4c:	e3a03000 	mov	r3, #0	; 0x0
  50:	e1a00003 	mov	r0, r3
  54:	e24bd00c 	sub	sp, fp, #12	; 0xc
  58:	e89da800 	ldmia	sp, {fp, sp, pc}
Disassembly of section .comment:

00000000 <.comment>:
   0:	43434700 	cmpmi	r3, #0	; 0x0
   4:	4728203a 	undefined
   8:	2029554e 	eorcs	r5, r9, lr, asr #10
   c:	2e342e33 	mrccs	14, 1, r2, cr4, cr3, {1}
  10:	Address 0x10 is out of bounds.

4.反汇编代码解析

在前面的汇编代码里面主要做了如下工作:

  • 设置栈
  • 调用main函数并把返回地址保存到lr寄存器中。

然后在C语言的main函数里面:

  • 定义两个局部变量,
  • 设置变量,
  • return 0

我们需要回答下面几个问题:

  1. 为什么要用start.s设置了栈才能调用C函数:因为C函数要用到栈。
  2. 怎么使用栈: a.栈可以用来保存局部变量。b.保存lr等寄存器。
  3. 调用者如何传参数给被调用者,
  4. 被调用者如何传返回值给调用者。
  5. 怎么从栈中恢复寄存器。

下面我们开始分析反汇编代码,假设我们的程序是从nandflash启动,那么nandflash前4K的内容就会被拷贝到片内4K内存。那么我们的机器码就是保存在4K内存里面。

4.1第一条指令

上电后从0地址开始执行,

 0:	e3a0da01 	mov	sp, #4096	; 0x1000

4.2第二条指令

eb000000 	bl	c <main>

然后是bl c  也就是跳转到main函数开始执行,同时让lr等于他的返回地址也就是下一条指令的地址,那么lr=8.

4.4第四条指令

   c:	e1a0c00d 	mov	ip, sp

这个就是把sp赋值给IP,然后ip等于4096.

4.5第五条指令

10:	e92dd800 	stmdb	sp!, {fp, ip, lr, pc}

这一条就是后面的fp, ip, lr, pc这四个寄存器的值保存在sp对应的内存地址中,其中stmdb中的db表示decrement before.然后搞编号的寄存器保存到高地址中,

fp的编号是r11,ip的编号是r12,lr寄存器的编号是r14,pc寄存器的是r15.

所以我们先存pc,pc等于当前指令地址+8, 也就是0x18. 

接下来存放lr寄存器,lr=8.

ip=sp=4096,

fp未知值。

4.6第六条指令

  14:	e24cb004 	sub	fp, ip, #4	; 0x4

fp=ip-4=4096-4=4092.

4.7第七条指令

18:	e24dd008 	sub	sp, sp, #8	; 0x8

sp = sp - 8 = 4080 - 8 = 4072.

4.8第八条指令

1c:	e3a03456 	mov	r3, #1442840576	; 0x56000000

r3 = 0x56000000

4.9第九条指令

20:	e2833050 	add	r3, r3, #80	; 0x50

r3=0x56000050

4.10第十条指令

24:	e50b3010 	str	r3, [fp, #-16]

然后r3保存到内存为fp-16的地方,fp-16=4092-16=4076 .也就是内存地址为4076的地方存的值是0x56000050。

这里就相当于是把局部变量  unsigned int *pGPFCON = (unsigned int *)0x56000050;  保存到了栈中。

4.11-4.13第11-13条指令

  28:	e3a03456 	mov	r3, #1442840576	; 0x56000000
  2c:	e2833054 	add	r3, r3, #84	; 0x54
  30:	e50b3014 	str	r3, [fp, #-20]

r3=0x56000000

然后r3=0x56000054.

然后把rc保存到fp-20的地方, fp-20=4072的地方,

这里就相当于把局部变量  unsigned int *pGPFDAT = (unsigned int *)0x56000054;  保存到了栈中。

4.14-4.16 第14-16条指令

  34:	e51b2010 	ldr	r2, [fp, #-16]
  38:	e3a03c01 	mov	r3, #256	; 0x100
  3c:	e5823000 	str	r3, [r2]

r2=fp-16,也就是把内存4076的地方取值保存到r2中,也就是r2=0x56000050.也就是pGPFCON。

然后r3=0x100,

然后把r3这个值存入r2所指的地方,

也就对应于C语言的   *pGPFCON = 0x100;。

4.17-4.19 第17-19条指令

  40:	e51b2014 	ldr	r2, [fp, #-20]
  44:	e3a03000 	mov	r3, #0	; 0x0
  48:	e5823000 	str	r3, [r2]

把4072内存中对应的0x56000054,然后把r3=0存入0x56000054中,这三条对应于C语言的  *pGPFDAT = 0;。

4.20-4.21 第20-21条指令

  4c:	e3a03000 	mov	r3, #0	; 0x0
  50:	e1a00003 	mov	r0, r3

r3=0,

然后把r3赋值给r0,那么r0=0,

这两句对应的是把C语言的return 0的返回值保存到r0中。

4.22第22条指令

  54:	e24bd00c 	sub	sp, fp, #12	; 0xc

sp = fp -12=4092-12=4080.

4.23第23条指令

 58:	e89da800 	ldmia	sp, {fp, sp, pc}

从栈中恢复寄存器,

fp=[4080]=原来保存的fp,

sp=[4084]=4096

pc=[4088]=8=调回0x8的地址。   #之前我们4088内存里面存的是lr寄存器的值,这样正好就把返回地址赋值给了PC,

 

栈:所谓栈就是sp寄存器所指向的内存。栈可以用来保存寄存器,保存局部变量。

猜你喜欢

转载自blog.csdn.net/u013171226/article/details/114749479