寄存器模型
寄存器是 CPU 内部最快速的存储单元,理解寄存器模型是学习 x86 汇编的基础。本章将详细介绍 x86 架构的各种寄存器。
寄存器概述
x86 架构的寄存器经历了从 16 位到 32 位再到 64 位的演变。现代 x86-64 处理器提供了丰富的寄存器资源。
寄存器分类
x86 寄存器主要分为以下几类:
- 通用寄存器:用于算术运算、逻辑运算和数据传送
- 段寄存器:用于内存分段管理
- 标志寄存器:存储处理器状态和运算结果标志
- 指令指针:指向下一条要执行的指令
- 控制寄存器:控制处理器行为(特权级操作)
- 调试寄存器:用于调试功能
通用寄存器
64 位通用寄存器
x86-64 架构提供了 16 个 64 位通用寄存器:
| 64 位寄存器 | 32 位 | 16 位 | 8 位低 | 8 位高 | 主要用途 |
|---|---|---|---|---|---|
| RAX | EAX | AX | AL | AH | 累加器、返回值 |
| RBX | EBX | BX | BL | BH | 基址寄存器 |
| RCX | ECX | CX | CL | CH | 计数器、循环计数 |
| RDX | EDX | DX | DL | DH | 数据寄存器、I/O |
| RSI | ESI | SI | SIL | - | 源变址寄存器 |
| RDI | EDI | DI | DIL | - | 目标变址寄存器 |
| RBP | EBP | BP | BPL | - | 栈帧基址指针 |
| RSP | ESP | SP | SPL | - | 栈指针 |
| R8 | R8D | R8W | R8B | - | 通用寄存器 |
| R9 | R9D | R9W | R9B | - | 通用寄存器 |
| R10 | R10D | R10W | R10B | - | 通用寄存器 |
| R11 | R11D | R11W | R11B | - | 通用寄存器 |
| R12 | R12D | R12W | R12B | - | 通用寄存器 |
| R13 | R13D | R13W | R13B | - | 通用寄存器 |
| R14 | R14D | R14W | R14B | - | 通用寄存器 |
| R15 | R15D | R15W | R15B | - | 通用寄存器 |
寄存器继承关系
x86 架构保持了向后兼容性,64 位寄存器的低 32 位、16 位和 8 位部分可以独立访问:
┌─────────────────────────────────────┐
│ RAX (64位) │
├─────────────────────┬───────────────┤
│ │ EAX (32位) │
├─────────────────────┼───────┬───────┤
│ │ AX │ │
│ │(16位) │ │
│ ├───┬───┤ │
│ │AH │AL │ │
│ │8位│8位│ │
└─────────────────────┴───┴───┴───────┘
访问低 32 位寄存器会自动将高 32 位清零:
mov rax, 0x123456789ABCDEF0
mov eax, 0 ; RAX = 0x0000000000000000(高32位被清零)
mov rax, 0x123456789ABCDEF0
mov ax, 0 ; RAX = 0x123456789ABC0000(仅低16位被修改)
传统寄存器的特殊用途
虽然现代 x86-64 中大多数寄存器都是通用的,但某些寄存器仍有特殊用途:
RAX/EAX - 累加器
- 算术运算的默认寄存器
- 函数返回值存放位置
- 乘除法的隐含操作数
mul rbx ; RAX = RAX * RBX(128位结果在 RDX:RAX)
div rbx ; RAX = RAX / RBX,RDX = 余数
RCX/ECX - 计数器
- 循环指令的计数器
- 字符串操作重复前缀的计数器
mov rcx, 10
loop_label:
; 循环体
loop loop_label ; RCX--,如果 RCX != 0 则跳转
RSI/ESI 和 RDI/EDI - 变址寄存器
- 字符串操作的源和目标指针
- 数组访问的常用寄存器
; 字符串复制
mov rsi, source ; 源地址
mov rdi, dest ; 目标地址
mov rcx, length ; 长度
rep movsb ; 重复复制字节
RSP/ESP - 栈指针
- 指向当前栈顶
- 自动被 push、pop、call、ret 等指令修改
push rax ; RSP -= 8,将 RAX 存入栈
pop rbx ; 从栈读取到 RBX,RSP += 8
RBP/EBP - 栈帧基址指针
- 指向当前栈帧的基址
- 用于访问局部变量和参数
push rbp
mov rbp, rsp
sub rsp, 32 ; 分配 32 字节局部变量空间
; 访问局部变量:[rbp-8], [rbp-16] 等
; 访问参数:[rbp+16], [rbp+24] 等
新增寄存器 R8-R15
x86-64 新增了 8 个通用寄存器 R8-R15,这些寄存器:
- 完全通用,没有特殊用途
- 减少了内存访问的需求
- 在调用约定中用于传递参数
mov r8, 100
mov r9, 200
add r8, r9 ; R8 = 300
段寄存器
段寄存器用于内存分段管理,在 16 位和 32 位保护模式下有重要作用。
段寄存器列表
| 寄存器 | 名称 | 用途 |
|---|---|---|
| CS | 代码段寄存器 | 存放代码段选择子 |
| DS | 数据段寄存器 | 存放数据段选择子 |
| SS | 栈段寄存器 | 存放栈段选择子 |
| ES | 附加段寄存器 | 字符串操作目标段 |
| FS | 附加段寄存器 | 线程本地存储 |
| GS | 附加段寄存器 | 线程本地存储 |
64 位模式下的段寄存器
在 x86-64 长模式下:
- CS、DS、ES、SS 的基址被强制为 0
- FS 和 GS 仍可用于线程本地存储(TLS)
- 大多数情况下不需要显式操作段寄存器
; 访问线程本地存储(Linux)
mov rax, fs:0 ; 读取 TLS 基址
标志寄存器
RFLAGS(64 位)或 EFLAGS(32 位)寄存器存储处理器状态和运算结果标志。
标志位布局
┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐
│...│IOPL│NT│...│RF│VM│AC│VIF│VIP│ID│ │ │ │ │ │ │ │
├───┴───┴───┴───┴───┴───┴───┴───┴───┴───┼───┼───┼───┼───┼───┼───┤
│ │OF│DF│IF│TF│SF│ZF│ │AF│
├───────────────────────────────────────┴───┴───┴───┴───┴───┼───┼───┤
│ │PF│ │CF│
└───────────────────────────────────────────────────────────┴───┴───┘
常用标志位
状态标志(反映运算结果)
| 标志 | 名称 | 含义 |
|---|---|---|
| CF | 进位标志 | 无符号运算溢出 |
| ZF | 零标志 | 结果为零 |
| SF | 符号标志 | 结果为负 |
| OF | 溢出标志 | 有符号运算溢出 |
| PF | 奇偶标志 | 结果中 1 的个数为偶数 |
| AF | 辅助进位标志 | 低 4 位进位 |
控制标志
| 标志 | 名称 | 含义 |
|---|---|---|
| DF | 方向标志 | 字符串操作方向(0=递增,1=递减) |
| IF | 中断标志 | 是否响应可屏蔽中断 |
标志位使用示例
; 加法运算影响标志位
mov rax, 0xFFFFFFFFFFFFFFFF
add rax, 1 ; 结果为 0
; CF = 1(进位)
; ZF = 1(结果为零)
; SF = 0(结果非负)
; OF = 0(无符号溢出,但有符号不溢出)
; 比较操作
cmp rax, rbx ; 执行 RAX - RBX,设置标志位但不保存结果
jl less_than ; 如果 SF != OF,跳转(有符号小于)
; 方向标志控制字符串操作
cld ; DF = 0,字符串操作递增
std ; DF = 1,字符串操作递减
条件码与标志位关系
| 条件码 | 含义 | 标志位条件 |
|---|---|---|
| ZF=1 | 等于/零 | ZF = 1 |
| ZF=0 | 不等于/非零 | ZF = 0 |
| CF=1 | 无符号小于/进位 | CF = 1 |
| CF=0 | 无符号大于等于 | CF = 0 |
| SF=OF | 有符号大于等于 | SF = OF |
| SF≠OF | 有符号小于 | SF ≠ OF |
指令指针
RIP(64 位)或 EIP(32 位)寄存器存储下一条要执行的指令地址。
RIP 的特点
- 不能直接修改,只能通过控制流指令间接修改
- 在顺序执行时自动增加
- 被跳转、调用、返回等指令修改
; 获取当前 RIP(x86-64 特有)
lea rax, [rip] ; RAX = 当前指令地址
; 相对跳转
jmp label ; RIP = label 地址
; 函数调用
call function ; RIP = function 地址,返回地址入栈
位置无关代码
RIP 相对寻址使得编写位置无关代码变得简单:
; 访问全局变量(位置无关)
lea rax, [rip + global_var] ; 加载全局变量地址
mov rbx, [rax]
; 调用函数(位置无关)
call [rip + function_ptr] ; 通过 RIP 相对地址调用
控制寄存器
控制寄存器用于控制处理器的操作模式,只能在特权级 0(内核模式)下访问。
主要控制寄存器
| 寄存器 | 名称 | 主要用途 |
|---|---|---|
| CR0 | 控制寄存器 0 | 保护模式使能、分页使能、缓存控制 |
| CR2 | 控制寄存器 2 | 页错误线性地址 |
| CR3 | 控制寄存器 3 | 页目录基址 |
| CR4 | 控制寄存器 4 | 各种架构扩展使能 |
; 读取控制寄存器(内核代码)
mov rax, cr0
; 写入控制寄存器
mov cr0, rax
模型特定寄存器(MSR)
MSR 是处理器特定的寄存器,用于控制和报告处理器性能。
; 读取 MSR
mov rcx, 0xC0000082 ; MSR 地址
rdmsr ; 结果在 EDX:EAX
; 写入 MSR
mov rcx, 0xC0000082
wrmsr ; 写入 EDX:EAX 到 MSR
调用约定与寄存器使用
System V AMD64 ABI(Linux/macOS)
| 寄存器 | 用途 | 保存者 |
|---|---|---|
| RAX | 返回值 | - |
| RDI | 第 1 个参数 | 调用者 |
| RSI | 第 2 个参数 | 调用者 |
| RDX | 第 3 个参数 | 调用者 |
| RCX | 第 4 个参数 | 调用者 |
| R8 | 第 5 个参数 | 调用者 |
| R9 | 第 6 个参数 | 调用者 |
| R10-R11 | 临时寄存器 | 调用者 |
| RBX | 通用寄存器 | 被调用者 |
| RBP | 栈帧指针 | 被调用者 |
| R12-R15 | 通用寄存器 | 被调用者 |
Microsoft x64 调用约定(Windows)
| 寄存器 | 用途 | 保存者 |
|---|---|---|
| RAX | 返回值 | - |
| RCX | 第 1 个参数 | 调用者 |
| RDX | 第 2 个参数 | 调用者 |
| R8 | 第 3 个参数 | 调用者 |
| R9 | 第 4 个参数 | 调用者 |
| R10-R11 | 临时寄存器 | 调用者 |
| RBX, RBP, RDI, RSI, R12-R15 | 通用寄存器 | 被调用者 |
函数调用示例
; Linux 调用约定
; int add(int a, int b, int c)
; 参数:RDI=a, RSI=b, RDX=c
; 返回值:RAX
add_three:
mov eax, edi ; EAX = a
add eax, esi ; EAX = a + b
add eax, edx ; EAX = a + b + c
ret
; 调用示例
mov edi, 10
mov esi, 20
mov edx, 30
call add_three ; RAX = 60
小结
本章介绍了 x86 架构的寄存器模型:
- 通用寄存器:16 个 64 位寄存器,支持 8/16/32/64 位访问
- 段寄存器:用于内存分段,64 位模式下作用有限
- 标志寄存器:存储运算结果标志和处理器状态
- 指令指针:指向下一条指令地址
- 控制寄存器:控制处理器行为(内核模式)
理解寄存器模型是编写高效汇编代码的基础。下一章将学习 x86 的基础指令。