【Linux】函数过程的调用

112 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第12天,点击查看活动详情

函数过程的调用

寄存器

  • %rax 通常用于存储函数的返回值,同时也用于乘法和除法指令。在乘法imul指令中,两个64位的乘积超过64位时,高位存储在%rdx中。在除法指令idiv中,被除数是超过64位时,高位存储在%rdx中。
  • %rsp是堆栈指针寄存器,指向的是栈顶。堆栈操作poppush就是通过改变%rsp的值实现。
  • %rbp是栈帧指针,用于标识当前栈帧的起始位置。
  • %rdi,%rsi,%rdx, %rcx, %r8, %r9用来存储函数调用的前6个参数,超出的部分放在堆栈中。

子函数调用过程

在子函数调用时,执行的操作有:

  • 父函数将调用参数从后向前压栈,即arg1在低地址,argn在高地址

  • 将返回地址压栈保存

  • 跳转到子函数起始地址执行

  • 子函数将父函数栈帧起始地址%rbp 压栈

  • %rbp 的值设置为当前 %rsp 的值,即将 %rbp 指向子函数栈帧的起始地址。

       pushq rbp         // 将调用函数的栈帧起始地址压入栈
       moveq rsp rbp     // 使得rbp 指向当前被调用函数的栈帧起始地址
    

call指令,同时完成了将返回地址入栈,以及跳转到子函数,但是调用函数的rbp需要子函数来保存,属于callee save

函数的返回

需要完成:

  • 恢复栈的结果到函数调用之前的状态
  • 跳转到调用函数的返回地址处继续执行

由于在调用子函数时已经保存了返回地址和调用函数的栈帧起始地址,那么只需要恢复即可:

     moveq %rbp, %rsp    // 使得 %rsp 和 %rbp 指向一处:子函数的栈帧起始地址 
     popq  %rbp          // 调用函数的栈帧起始地址地址,并且%rsp上移一个位置,指向返回地址

此时,再调用ret指令,其作用就是从当前%rsp指向的位置,即栈顶,弹出数据,并且跳转到此数据表示的地址处。此时调用ret执行

  • 会弹出栈中的数据,返回主函数,
  • 使得%rsp再上移动一个位置,使得%rsp指向调用函数的栈帧的结尾处。