寄存器
%rax
通常用于存储函数的返回值,同时也用于乘法和除法指令。在乘法imul
指令中,两个64位的乘积超过64位时,高位存储在%rdx
中。在除法指令idiv
中,被除数是超过64位时,高位存储在%rdx
中。%rsp
是堆栈指针寄存器,指向的是栈顶。堆栈操作pop
、push
就是通过改变%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
指向调用函数的栈帧的结尾处。