C语言内部机制分析—基于S3C2440c语言点灯

代码

汇编

之前的汇编代码如下

//这些只是汇编的语法而已,没什么好记的
.text
.global _start
_start:
/*设置内存:sp(堆栈指针) 栈*/
	ldr sp, = 4096 //nand
//	ldr sp, = 0x40000000 + 4096/*nor启动*/
/*调用main函数*/
	bl main /*跳转过去执行main,并且把返回地址保存起来*/
halt:
	b halt

流程如下

  • 1、设置栈
  • 2、调用main,并把返回地址保存在lr中

main.c

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

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

	/*配置GPF4输出0*/
	*pGPFDAT = 0;
	return 0;
}

流程如下

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

疑问

  • 为何要设置栈才能调用main函数?
    ——C函数要用

  • 怎么使用栈?
    ——保存局部变量、保存lr等寄存器
    汇编中bl main的返回值存放在lr寄存器中。
    假设main函数调用其他函数,调用完之后也应该返回在mian函数中继续执行。
    显然,在调用子函数的同时,也应该将返回地址保存在lr里面。那lr中main函数的地址不就被子函数的返回地址覆盖了吗?——所以会保存lr等寄存器

  • 调用者如何将参数传递给被调用者(子函数)?

  • 被调用者如何将返回值返回给调用者?
    这个两问题就需要去了解一下,汇编调用C函数所遵循的ATPCS规则
    ARM-THUMB prodcedure call standard(ARM-Thumb过程调用标注)
    在这里插入图片描述这四个寄存器是参数结果寄存器
    调用者将参数传递给被调用者,就是通过这几个寄存器
    被调用者将结果返回给调用者,也是通过这几个寄存器

  • 怎么从栈中恢复那些寄存器?
    在这里插入图片描述
    在函数中,r4~r11可能被使用
    所以,在函数的入口保存他们,在函数的出口保存他们
    当然,你的函数可能非常简单,那么用到谁就保存谁就行了

分析反汇编代码

在这里插入图片描述
假设我们使用的是nand启动
对nand启动的程序,硬件上会将Nand Flash前4k的内容完完全全的拷贝到片内的4
k内存
也就是说,第二列那些机器码会完完全全的保存在4k内存的前面

内存中的状态如下:
在这里插入图片描述
最后一个地址是e89da800
可见,内存中没有保存common段
common段是程序的注释部分,bin文件中不需要包含这些注释

在这里插入图片描述

流程

在这里插入图片描述

一上电,从0地址开始执行,将栈设置在地址4096
接着bl将返回地址保存在0x8,并且跳转到0x0c开始执行main函数

在这里插入图片描述
一开始,ip = sp = 4096

第二条语句是把括号中四个寄存器的值保存在sp对应的内存里面
stmdb,先减后存,根据寄存器的标号来存。fp,ip,lr,pc对应标号分别为r11、r12、r14、r15.高标号放在高地址。所以我们先存放的是pc的地址,pc = 当前指令的地址 + 8,即0x18。
接下来存放lr,lr = 0x8
ip = sp = 4096
fp是未定义值
以上是这四个寄存器的值,存放的地址是从4096依次减4
所以最后的sp’ = 4096 - 4*4 = 4080

第三条指令
fp = ip - 4 = 4092

第四条
sp = sp - 8 = 4080 - 8 = 4072

第五第六第七
r3 = 0x56000050
r3 的值存放在【fp-16】=【4076-16】=4060
在这里插入图片描述
这就是pGPFCON局部变量,局部变量存放在栈中

第10条
将r3的值0x56000054,存入[fp - 20] = 4072的地方
在这里插入图片描述
就是这个局部变量

第11条
r2 = [fp - 16] = [4076] = 0x56000050 = pGPFCON

第12条
将r3—0x100存入r2所指的地方-----0x56000050
对应的c语言就是
在这里插入图片描述

第13条
r2 = [fp - 20] = 4072 = 0x56000054

14条
把r3存入r2所指的地方就是0x56000054
在这里插入图片描述
对应的C代码是这里↑

后面两条
将0赋值给r3
将r3赋值给r0
编译器不是很聪明,其实把0直接赋值给r0即可
被调用者将返回值返回给调用者,需要通过r0~r3。这边的返回值只有一个,所以用r0就可以了

倒数第二条
恢复栈
sp = fp - 12 = 4092 - 12 = 4080

最后一条
从栈中恢复寄存器
fp,sp,pc标号分别为r11,r13,r15
fp = [4080] = 原来保存的fp
sp = [4084] = 4096
pc = [4088] = 8—跳回到0x8的地址
在这里插入图片描述
返回到这里↑

总结

由上面的分析可知,栈就是sp寄存器所指向的内存,可读可写才行
在这里插入图片描述
在前面的部分用来保存寄存器,在函数返回之前会恢复寄存器
在这里插入图片描述
这边就是局部变量
在这里插入图片描述
这里也可以看出来,8字节就是局部变量的空间

在这里插入图片描述
将栈设置在4096是完全没问题的。
栈是由高地址转为低地址,程序非常小,栈并不会破坏底下main函数

这个程序中我们只涉及到被调用者向调用者返回返回值,那么调用者怎么传参数给被调用者呢?

下一篇博文编写程序测试

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Spark!

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值