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置零。