数据格式
操作数格式
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
指令的原理
- 堆栈指针的调整:
- 在x86架构中,堆栈向低地址方向增长。
- 执行
push
指令时,首先将堆栈指针ESP
(或在x86_64架构中为RSP
)减小,以为新的数据腾出空间。对于32位系统,堆栈指针减小4个字节;对于64位系统,堆栈指针减小8个字节。
- 数据写入堆栈:
- 将要压入堆栈的数据写入新的堆栈指针位置。
pop指令
pop
指令的原理
- 数据从堆栈弹出:
- 从当前堆栈指针位置读取数据。
- 堆栈指针的调整:
- 在x86架构中,堆栈向低地址方向增长。
- 执行
pop
指令时,将堆栈指针ESP
(或在x86_64架构中为RSP
)增加,以释放之前存储的数据空间。对于32位系统,堆栈指针增加4个字节;对于64位系统,堆栈指针增加8个字节。
call指令
call原理
保存返回地址:
call
指令会将下一条指令的地址(即返回地址)压入堆栈。
跳转到子程序:
call
指令会将程序计数器(PC,x86架构中为EIP
或RIP
)设置为子程序的地址,从而跳转到子程序执行。
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中详细定义了这些行为。