函数调用与返回
2022-06-28 09:00:00

数调用与返回,一般来说也可以称作『过程』。

过程与栈

过程,存在以下三种机制:

  • 转移控制:过程 A 调用过程 B,将程序计数器(PC)设置为过程 B 的起始地址;返回时,PC 设置为过程 A 调用过程 B 后面的指令的地址;
  • 传递数据:过程 A 向过程 B 传递一个或多个参数,过程 B 返回一个值;
  • 分配和释放内存:过程 B 需要为局部变量分配内存空间,返回前需要释放这些内存空间。

『分配和释放内存』则通过『』来实现。程序通过栈管理过程所需的内存空间,栈和 PC 记录着三种机制所需的信息。每一个过程生成一个『栈帧』。

栈中保存了各种函数调用时的信息,而代码段中存储运行所需的机器指令,并通过程序计数器 PC 记录运行过程中机器指令的内存地址。

栈帧则是一个函数调用过程中保存的各种信息,并通过基址指针%ebp和栈指针%esp确定其在栈中的范围。

stack_frame

①:%rsp通过汇编指令pushpop执行出入栈操作,入栈内存地址减小,出栈内存地址增大;

②:%rbp指向的是保存过程 A 的栈帧的帧指针,同时与过程 B 的%rsp标定栈帧范围;

③:过程 A 在调用过程 B 时,通过寄存器最多传送 6 个参数,如果还有多的参数,通过栈传出;

④:过程 B 返回后,PC 通过返回地址,得到继续运行的指令地址。

实际上,如果参数个数少于 6 个,且不会调用其他过程,则过程 B 无需栈帧,局部变量可以保存在寄存器中。

调用过程

假设有过程 A 调用了过程 B,经过以下几个步骤:

过程 A 为调用者,过程 B 为被调用者。

  1. 调用者保存参数,参数少于 6 个,直接存入寄存器,第 7 个及更多的参数保存在调用者的栈帧中;

    前 6 个参数分别保存在%rdi%rsi%rdx%rcx%r8%r9中;其余参数在栈帧中的位置见上图。

  2. 调用者将调用指令的下一个指令的地址,作为返回地址,存出调用者栈帧;

  3. 被调用者将调用者的基址指针%rbp保存,以便返回后恢复调用者的栈帧范围;

  4. 被调用者设置自己的基址指针和栈指针指向同一个位置;

  5. 如果需要分配给被调用者分配栈,则栈指针下移,最终基址指针和栈指针记录了被调用者的栈帧范围;

  6. 被调用者产生局部变量时,保存到栈帧中,栈与数据段和代码段的区别

  7. 通过 PC 执行被调用者的机器指令。

被调用者执行完成后,销毁其栈帧:

  1. 栈指针回退到基址指针,基址指针读取旧的%ebp,恢复到调用者的栈帧范围;
  2. 返回地址 pop 到 PC 中,获取调用前机器指令的地址;
  3. 完全恢复到调用前的状态,继续执行。