栈的概念
所谓的栈就是一块空间的内存,CPU的SP寄存器指向它,它可以用于函数调用,局部变量,多任务系统里保存现场。
使用C语言实现 栈
volatile int num=0;
int fun_b(volatile int a)
{
a = a+2;
return a;
}
int fun_c(volatile int a)
{
a = a+2;
return a;
}
void fun_a(volatile int a)
{
num = fun_b(a);
num = fun_c(num);
}
int main(void)
{
fun_a(99);
return 0;
}
代码中函数调用关系:
main -> fun_a
fun_a -> fun_b
-> fun_c
通过代码反汇编解析 栈
编译代码时,制作反汇编步骤: 在配置中添加命令
fromelf --text -a -c --output=xxx.dis xxx.axf
其中的xxx.axf文件在linker中获取
运行后反汇编文件在工程目录下。查看反汇编代码:
main函数中使用BL命令跳转到fun_a函数(pc=0x80002d0,lr=0x08000308)
fun_a函数中使用BL命令跳转到fun_b函数(pc=0x80002ec,lr=0x080002d8),运行完fun_b函数后,使用BL命令跳转到fun_c函数(pc=0x80002f6,lr=0x080002e2)
在上面的过程中发现lr的值会被覆盖,若被覆盖就会导致找不到之前函数返回的地方。因此在每个函数的第一条反汇编会把lr值保存到栈中(使用push命令),当函数结束的时候使用pop命令回到之前的位置。
整个栈过程如下图所示:
RAM架构
为了更好的理解栈的作用,可以结合RAM架构以及汇编语言。
SOC芯片上有很多模块:CPU(主要运算数据)、RAM(内存,数据存储)、Flash(指令存储)等其他模块。
计算a+b,需要经过四个步骤:
1、从RAM中取出a值;
2、从RAM中取出b值;
3、在CPU 运算单元模块 运算;
4、将运算结果存到RAM中的a位置
CPU中有15个寄存器(R0—R15),其中普通寄存器R0~R12是存储数据;
特殊寄存器R13别名为SP:栈;
特殊寄存器R14别名为LR:返回地址;
特殊寄存器R15别名为PC:当前指令地址;
CPU 从RAM中取出来的ab数值 会存入R0~R12中的任意寄存器中。
CPU要执行a+b的功能,就需要从Flash中获取指令。如下图所示,
1、程序员将编写好的程序编译成执行文件,使用特有的烧写工具将执行文件烧写到Flash中,因此在Flash保存就是机器语言。
2、CPU根据R15(当前指令地址)从Flash中读取指令到CPU,执行指令(a+b)
3、CPU根据汇编中的地址取出运算中要使用的ab值,保存在CPU的寄存器中
4、CPU从寄存器中取出值进行运算,并将计算结果保存到RAM中(a地址)
汇编语言
根据上述的反汇编内容,理解下指令a+b的过程。使用上面的fun_c函数为例。
读指令 LDR (Load 加载) :LDR r0, [ addr A ]
描述:将A地址读到R0寄存器(4字节)
LDRH(2字节)
LDRB(1字节)
写指令 STR (Store 存储):STR r0, [ addr A ]
描述:将R0地址写到A地址中(4字节)
运算符 ADDS:ADDS r0,r1,r2
描述:r1+r2,相加的值存入r0寄存器中
入栈 PUSH:PUSH { r0, LR }
描述:将r0,LR的值压入栈中,本质就是向RAM写两次数据。其中存入RAM的地址就是特殊寄存器R13 sp值
假设sp的地址为0x100,则压栈的时候 根据 “高标号的寄存器,存在高地址中”
(注: { } 中的顺序无关)
SP=SP-4;LR存入SP所指向的地址;SP=SP-4;R0存入SP所指向的地址(SP-4);SP=SP-4
出栈 POP:POP { r3, PC }
描述:从栈中取出值赋给r3,PC,本质就是向RAM读两次数据。其中从SP当前所指向的地址开始读数据。
从SP所指的栈地址取出数据赋给 r3;SP= SP+4;从当前SP所指的栈地址取出数据赋给PC;SP= SP+4