关于寄存器信息与stack结构和函数调用关系(callee saved and caller saved)

数据格式

操作数格式


PS:可能的数据传输方向

  • m to r
  • r to m
  • r to r

m to m是不可能的,无法实现的如果要实现m to m,先m to r 再 r to m

整数寄存器结构

## 关于栈指针 ESP

指向的是栈的顶部。指向栈的低位。栈的增长方向是从高地址往低地址增长。

关于调用者保存和被调用者保存

push指令

push指令的原理

  1. 堆栈指针的调整
    • 在x86架构中,堆栈向低地址方向增长。
    • 执行push指令时,首先将堆栈指针ESP(或在x86_64架构中为RSP)减小,以为新的数据腾出空间。对于32位系统,堆栈指针减小4个字节;对于64位系统,堆栈指针减小8个字节
  2. 数据写入堆栈
    • 将要压入堆栈的数据写入新的堆栈指针位置。

pop指令

pop指令的原理

  1. 数据从堆栈弹出
    • 从当前堆栈指针位置读取数据。
  2. 堆栈指针的调整
    • 在x86架构中,堆栈向低地址方向增长。
    • 执行pop指令时,将堆栈指针ESP(或在x86_64架构中为RSP)增加,以释放之前存储的数据空间。对于32位系统,堆栈指针增加4个字节;对于64位系统,堆栈指针增加8个字节

call指令

call原理

保存返回地址

  • call指令会将下一条指令的地址(即返回地址)压入堆栈。

跳转到子程序

  • call指令会将程序计数器(PC,x86架构中为EIPRIP)设置为子程序的地址,从而跳转到子程序执行。

ret指令

ret 指令用于从函数返回。它的主要作用是:

  • 从栈中弹出返回地址。
  • 跳转到返回地址继续执行。

被调用者保存(callee-saved)

定义:

被调用者保存的寄存器是在函数调用期间,由被调用的函数负责保存和恢复的寄存器。这意味着在函数调用之前,这些寄存器的值可以被调用者(调用函数)依赖,即在调用之后,寄存器的值不会改变。

callee_function:
    push ebx            ; 保存ebx寄存器的值
    push esi            ; 保存esi寄存器的值
    push edi            ; 保存edi寄存器的值

    ; 执行函数主体

    pop edi             ; 恢复edi寄存器的值
    pop esi             ; 恢复esi寄存器的值
    pop ebx             ; 恢复ebx寄存器的值
    ret

调用者保存(caller-saved)

定义

调用者保存的寄存器是在函数调用期间,由调用者负责保存和恢复的寄存器。这意味着在函数调用之前,调用者必须保存这些寄存器的值,如果需要的话,在调用之后恢复它们。

caller_function:
    push eax            ; 保存eax寄存器的值
    push ecx            ; 保存ecx寄存器的值
    push edx            ; 保存edx寄存器的值

    call callee_function

    pop edx             ; 恢复edx寄存器的值
    pop ecx             ; 恢复ecx寄存器的值
    pop eax             ; 恢复eax寄存器的值

    ; 继续执行调用者的代码

从寄存器结构理解二者区别

调用者保存和被调用者保存的堆栈上的区别在于,调用者保存的返回地址会位于一个低位,而被调用者的返回地址会位于保存的寄存器的高位。

调用者保存(Caller-Saved)

  • 调用者在调用函数之前保存需要保留的寄存器值到堆栈。
  • 保存寄存器后,再将返回地址压入堆栈。
  • 进入被调用函数时,堆栈顶部为返回地址,之后是调用者保存的寄存器。

被调用者保存(Callee-Saved)

  • 进入被调用函数后,被调用者保存需要保留的寄存器值到堆栈。
  • 返回地址通常在保存寄存器之前已经在堆栈中。

被调用者保存的堆栈布局

| high address  |
| ...           |
| r15           | <-- Callee-saved
| r14           |
| r13           |
| r12           |
| rbp           |
| rbx           |
| return address| <-- Top of the stack after 'call'
| low address   |

调用者保存的堆栈布局

| high address  |
| ...           |
| rdx           | <-- Caller-saved
| rcx           |
| rax           |
| return address| <-- Top of the stack after 'call'
| low address   |

计算机如何决定使用callee-saved或者caller-saved?同时如何决定数据位于寄存器或者堆栈上?

calling conventions中详细定义了这些行为。