文章目录
#栈帧的形成和关闭
栈在内存中是一块特殊的存储空同, 它的存储原则是“先进后出”, 即最先被存储的数据最后被释放, 汇编过程通常使用 push 指令与 POP指令对栈空间执行数据压入和数据弹出操作。
栈结构在内存中占用一段连续的存储空间, 通过sp与 fp这两个栈指针寄存器(在x86上是esp,ebp)来保存当前栈的起始地址与结束地址(又称为 栈顶与 栈底)。 在 栈结构中, 每4字的 栈空间保存一个数据, 像这样的 栈顶到 栈底之间的存储空间被称为 栈帧.
在arm中没有x86那样丰富的栈操作指令,也没有专门的栈指针寄存器, 按照PCS规定用
$r13
寄存器来作为sp寄存器,还有一个可选的$r11
作为fp寄存器。
栈帧是如何形成的呢? 当栈顶指针 sp小于栈底指针 fp时, 就形成了栈帧。 通常, 栈帧中可以寻址的数据有局部变量、函数返回地址等(关于栈帧中函数返回地址的寻址可见我以前博文mips体系堆栈回溯分析与实现)。
不同的两次函数调用, 所形成的栈帧也不相同 。 当由一个函数进入到另一个函数中时, 就会针対调用的函数开辟出其所需的栈空间, 形成此函数的栈。 当这个函数结束调用时, 需要清除掉它所使用的栈空同, 关闭栈帧,我们把这一过程称为栈平衡。
为什么要进行栈平衡呢?这就像借钱一样,’‘有惜有还.再借不难’。如果某一函数在开辞了新的栈空间后没有进行恢复, 或者过度恢复, 那么将会造成栈空间的上溢或下溢, 极有可能给程序带来致命性的错误
现在的高级语言中,没有让程序猿操作栈的机会,都是由编译器自动进行栈帧的开辟和释放操作,一般除了缓冲区溢出,或者强制指针操作不会导致栈的异常。
还以上文的代码为例子:
#include <iostream>
using namespace std;
int main()
{
// 将变量 nConst 修饰为const
const int nConst = 5;
// 定义int 类型的指针,保存nConst 地址
int *pConst = (int*)&nConst;
// 修改指针pConst 并指向地址中的数据
*pConst = 6;
// 将修饰为const 的变量nConst 赋值给nVar
int nVar = nConst;
cout << nVar << endl;
}
反汇编代码如下:
000091fc <main>:
91fc: e92d4800 push {fp, lr}
9200: e28db004 add fp, sp, #4
9204: e24dd010 sub sp, sp, #16
9208: e3a03005 mov r3, #5
920c: e50b3010 str r3, [fp, #-16] ;nConst = 5;
9210: e24b3010 sub r3, fp, #16
9214: e50b3008 str r3, [fp, #-8] ;pConst = &nConst
9218: e51b3008 ldr r3, [fp, #-8] ;r3 = pConst
921c: e3a02006 mov r2, #6 ;r2 = 6
9220: e5832000 str r2, [r3] ;*pConst = r2 = 6;
9224: e3a03005 mov