cmu15213 L7 Machine-Level Programming III: Procedures
ABI (application binary interface)应用程序二进制接口
文章目录
Procedures
Mechanisms
Stack Structure
教授提到他也不知道为啥栈要倒着这样画hhhh,栈的画法是高地址在上,低地址在下,栈顶是低地址,栈从高地址向低地址扩展。
入栈的时候 %rsp
减 8,即栈指针向低地址移动。
执行顺序是先把 %rsp
减 8 ,然后再把要存的书写到 %rsp
对应的地址中。(要先减指针,再写)
popq
的话是先读 %rsp
地址中的数,然后 %rsp
减 8,再把数存到寄存器中。(先读,后增加指针)
Calling Conventions
Passing control
参数依次存放在 %rdi,%rsi,%rdx
中,函数的返回结果存放在 %rax
中。
call,ret
这两个指令并没有完成过程调用与返回的全部任务,只完成了控制部分,而这一部分只是过程调用的三个方面之一。
有时候还会看到 rep;ret
,可以直接忽略掉。
执行 callq
之前,栈顶指针 %rsp=0x120
,当前执行指令指针 %rip=0x400544
callq
会执行三件事情(-8 是因为地址是 64 位的,并且 push 和 pop 也只支持 64 位操作):
- 减少栈指针(-8),即
%rsp
从0x120
变为0x118
- 将
callq
下面语句的地址(callq
下面的mov
地址为0x400549
)存到%rsp
指向的内存中,即栈顶。这个地址即为返回时需要继续执行的地址。 - 将
%rip
置成调用函数所在的地址(0x400550
,这是call
的隐式操作,我们并不能直接操作%rip
的值)
retq
则执行和 call
相反的操作:
- 首先读取栈顶的元素,即为返回地址
- 栈顶指针
%rsp
加 8 - 将返回地址给
%rip
这样就到了原来call
之后需要执行的语句了
Passing data
Managing local data
%rbp
是可选的,他不一定被用作栈基指针,一般会当成一个普通的寄存器来用。
这里栈会多分配一点,是为了对齐。
long
是 8 字节,这里用 movl $3000, %esi
实际上只是用了四个字节,并且用的不是 %rsi
而是 %esi
,此时 %rsi
的高 32 位会自动置零。编译器喜欢这么干的原因是 movl
的编码比 movq
的编码少一个字节。
这些寄存器是 caller-saved,意思是在调用函数之前要把这些寄存器压栈,调用函数后再恢复这些寄存器,而在被调用的函数中可以随意修改这些寄存器(因为他们已经在调用前被储存了起来)。
这些是 callee-saved ,意思是在被调用的函数中,假如想要使用这些寄存器的话,要先把他们压栈,函数结束后再恢复他们。
Illustration of Recursion
testq a, b
就是测试 a&b
%rbx
是 calle-svaed,所以在使用这个寄存器之前要 push,调用完子函数后要再恢复 %rbx