栈
栈是一种后进先出(LIFO)的数据结构,用于存储函数调用的相关信息,如局部变量、返回地址和保存的寄存器值。
在x86-64架构中,栈通常向下增长,即从高地址向低地址扩展。
函数调用
函数调用涉及以下步骤:
- 参数传递:前六个整数或指针参数通过寄存器
%rdi,%rsi,%rdx,%rcx,%r8,%r9传递,超过六个参数的通过栈传递(参数7位于栈顶) - 保存返回地址:调用指令
call执行两个操作:将返回地址(下一条指令的地址)压入栈中,并跳转到被调用函数的地址 - 函数体执行:被调用函数执行其代码,可能会使用栈来存储局部变量和保存寄存器
- 返回值传递:函数体执行完成后,返回值通过寄存器
%rax传递回调用者 - 函数返回:调用指令
ret执行两个操作:弹出(读取并释放)当前%rsp指向栈顶内存储的返回地址,并跳转到该地址执行(这要求函数必须释放所有其占用的栈空间,以将%rsp恢复到调用前的状态),
ABI
ABI(应用二进制接口)定义了函数调用约定,包括参数传递、返回值传递、寄存器使用和栈帧布局等规则。
- callee-saved:被调用函数必须保存和恢复的寄存器(如
%rbx,%rbp,%r12-%r15)。调用者可以认为这些寄存器在函数调用前后保持不变。 - caller-saved:如
%rax,%rcx,%rdx,%rsi,%rdi,%r8-%r11。被调用函数可以自由使用这些寄存器,不保证调用前后的值保持不变。