函数调用过程中的栈帧分析

说到函数栈帧,就不得不提到另外一个名词“”,栈的主要特点:先入后出,后入先出(就像叠罗汉一样),增长的方向,高地址向低地址生长,系统自动回收

那么栈帧是什么呢?

栈帧首先是存储在栈上,栈帧记录函数调用过程的一些信息的,例如变量、上一级栈帧的帧指针、函数的返回地址等,一个程序不止一个函数,很多时候都是函数的嵌套调用,那么栈上面肯定有多个栈帧(栈溢出因为栈空间不够,栈帧太多会消耗太多的栈空间,因为每个栈帧都要记录上一级栈帧的帧指针和返回地址),下图为栈帧的一般结构


可以看到上图有一个ebp,这个在x86中,指的是一个寄存器,还有另外一个寄存器esp和它一起使用

ebp(rbp):帧指针或基址指针,用来寻址,始终是指向栈帧的起始位置

esp(rsp):栈地址,会随着数据的入栈(push)和出栈(pop)移动,指向栈帧的顶部位置

eip(rip):指向cpu下一条指令的地址

举例之前,才来看一张图,这张图,对下面通过汇编分析函数的调用过程,有一定的作用


main.c的例子代码

int add(int a, int b)
{
        return (a + b);
}
int mul(int x, int y)
{
        return (x * y);
}
void fun(int x, int y)
{
        int sum = 0;
        int acc = 0;

        sum = add(x, y);
        acc = mul(x, y);
}
int main(int argc, char** argv)
{
        int a = 0;
        int b = 0;
        int ret = 0;
        a = 3;
        b = 4;
        fun(a, b);

        return 0;
}

这个程序的反汇编代码


分析这个例子的函数调用过程的栈帧

首先用gdb调试该程序,看一下调用fun函数之前,main函数的栈帧


进入fun函数的时候,栈帧的变化


进入add函数时,栈帧的变化


能把上面的入栈看懂,下面的出栈过程就简单了

首先是pop %rbp,这个主要是把上一级的栈帧送入rbp,就是让这个rbp这个帧指针指向原来的栈帧起始地址,rsp再向上移动8个字节

ret指令则是将栈顶的返回地址弹出到rip,然后按照rip此时指示的指令地址继续执行程序


leave指令主要是因为当前rsp和rbp指向的位置不一样,需要先将rsp = rbp,可以看到leave的指令都没有了pop %rbp这条指令,所以leave等价于:mov %rbp, %rsp ;pop %rbp

至此全部讲完,主要是借助了gdb来分析函数调用的栈帧

猜你喜欢

转载自blog.csdn.net/follow_blast/article/details/80824712