函数调用过程
- 当主函数调用子函数的时候:
- 在主函数中,将子函数的参数按照一定调用约定(参考调用约定),一般是从右向左把参数push到栈中;
- 然后把下一条指令地址,即返回地址(return address)push入栈(隐藏在call指令中);
- 然后跳转到子函数地址处执行: call 子函数;
- 此时子函数执行:
-
push %rbp;
把当前rbp的值保持在栈中; -
mov %rsp, %rbp;
把rbp移到最新栈顶位置,即开启子函数的新帧; -
[可选]
sub $xxx, %esp;
在栈上分配XXX字节的临时空间。(即抬高栈顶,编译器根据函数中的局部变量的总大小确定临时空间的大小) -
[可选]
push XXX;
保存(push)一些寄存器的值;
- 子函数调用返回:
- 保持返回值:一般将函数函数值保持在eax寄存器中;
- [可选] 恢复(pop)一些寄存器的值;
-
mov %rbp,%rsp;
收回栈空间,恢复主函数的栈顶; -
pop %rbp;
恢复主函数的栈底; -
ret;
从栈顶获取之前保持的返回地址(return address),并跳转到此位置执行;
函数帧
image.png
栈的状态变化
image.png