函数栈的变化过程

  • Post author:
  • Post category:其他


C++调用函数在内存管理中的过程就是新建一个函数栈,里面保存了函数返回值,寄存器的备份(rbp),局部变量,函数参数。调用结束后栈空间销毁,回到调用的函数继续执行下面的代码。

在这里插入图片描述

下面说几个要点。

首先要了解寄存器的作用。

rbp保存函数栈底指针,rsp保存函数栈顶指针,

rdi、rsi、rdx、rcx、r8、r9:调用函数时依次存放第1到第6个参数,若多于6个的参数则会被压入栈。

rip保存返回地址。

rax保存返回值。

我们从调用开始到调用结束一步步分析一下:(假设main函数调用add函数)

当我们调用函数时,栈的变化如下:

1、首先,会将原函数main函数的返回地址压入栈中,返回地址就是add函数下一条指令的地址,因为函数返回后就要执行这个指令了。

2、将原函数main的rbp栈底指针入栈,因为函数返回后还要维护main函数的栈底指针,main函数并没有执行完成。

3、将栈顶指针rsp的值赋给rbp,然后将栈顶指针减去一定的数值,为新的函数开辟空间。这里要注意,栈顶指针永远指向栈顶,所以当把rsp赋给rbp时,rbp已经指向原来main的栈顶+返回地址+main的rbp了那个位置了。

4、上面都是准备工作,接下来就是在那段空间进行一系列操作。比如存储函数参数的值(movl $1 %rbp-4就是将参数1放到栈底的上一个位置,栈是高地址向低地址增长的,所以要减),将值赋给寄存器等。如果有返回值,返回值会放到eax寄存器中。

5、栈中弹出main函数栈的rbp地址,赋值给rbp寄存器,即恢复main函数的rbp.

6、leave,RET退出调用。ret指令则是将栈顶的返回地址弹出到EIP,然后按照EIP此时指示的指令地址继续执行程序。leave指令相当于将rbp赋给rsp,并把rbp出栈,将里面的值赋给rbp寄存器(相当于步骤5)

可以看到5,6和2,1是配对的,因为栈是后进先出。

7.最后还可能有返回值,从eax取出赋给main函数的局部变量处,将eax置零。



版权声明:本文为weixin_53344209原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。