代码
汇编
之前的汇编代码如下
//这些只是汇编的语法而已,没什么好记的
.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函数
这个程序中我们只涉及到被调用者向调用者返回返回值,那么调用者怎么传参数给被调用者呢?
下一篇博文编写程序测试