flags registers
如果说通用寄存器是 CPU 的“工作台”,那么标志寄存器(Flags Register)就是 CPU 的“状态面板”。
在 x86-64 架构中,它被称为 RFLAGS。它不像 RAX 那样用来存一个具体的数值(如 100),而是把 64 位拆开,每一位(Bit)代表一个“开关”。当 CPU 执行完一条指令后,会根据结果自动拨动这些开关。
这些开关(标志位)直接决定了程序接下来的走向(比如 if-else 的跳转)。
一、 标志寄存器的层级
- FLAGS: 16 位(早期的 8086)
- EFLAGS: 32 位
- RFLAGS: 64 位(现代 64 位系统,高 32 位目前保留未用)
二、 最常用的“状态标志位”(重点)
作为初学者,你只需要重点掌握以下 4 到 5 个标志位,它们是汇编逻辑跳转的基石。
1. ZF (Zero Flag) —— 零标志
- 规则:如果运算结果是 0,则
ZF = 1;如果结果不是 0,则ZF = 0。 - 用途:用于判断两个数是否相等。
- 底层原理:当你执行
CMP eax, ebx时,CPU 实际上是在做减法。如果eax == ebx,结果就是 0,ZF 就会被置为 1。
2. CF (Carry Flag) —— 进位标志
- 规则:在无符号数运算中,如果产生了进位(加法)或借位(减法),则
CF = 1。 - 比喻:就像你用 8 位存数据(最大 255),算
255 + 1,结果超出了范围,CF 就会亮起,提醒你“溢出”了。
3. OF (Overflow Flag) —— 溢出标志
- 规则:在有符号数(补码)运算中,如果结果超出了能表达的范围,则
OF = 1。 - 区别:
- CF 关注的是物理上的进位(针对无符号数)。
- OF 关注的是逻辑上的错误(针对有符号数,比如两个正数相加居然得到了一个负数)。
4. SF (Sign Flag) —— 符号标志
- 规则:结果为负数时
SF = 1,结果为正数时SF = 0。 - 底层原理:它其实直接复制了运算结果的最高位。
5. DF (Direction Flag) —— 方向标志
- 规则:控制字符串操作指令(如
MOVS)。DF = 0表示正向(地址增加),DF = 1表示反向(地址减少)。
三、 核心指令:如何改变标志位?
并不是所有指令都会改变标志位(例如 MOV 通常不改变标志位),以下指令是改变标志位的主力军:
ADD / SUB:算术运算,会影响所有的状态标志。CMP(Compare):极其重要。它的本质是SUB,但它不保存运算结果,只为了拨动标志位。TEST:本质是AND(逻辑与)。常用于检查某个寄存器是否为 0(例如TEST EAX, EAX)。
四、 实战:标志位如何控制程序流程?
汇编中的“跳转指令”(Jcc)就是通过读取标志位来工作的:
| 指令 | 含义 | 检查的标志位 |
|---|---|---|
| JE (Jump Equal) | 相等则跳转 | ZF == 1 |
| JNE (Jump Not Equal) | 不相等则跳转 | ZF == 0 |
| JG (Jump Greater) | 大于则跳转(有符号) | ZF == 0 且 SF == OF |
| JB (Jump Below) | 低于则跳转(无符号) | CF == 1 |
五、 在 GDB 中观察标志位
回到你之前的 GDB 界面。当你在 GDB TUI 模式下查看寄存器时,通常会看到一个叫 eflags 的值(例如 0x246)。
如何看懂它? 在 GDB 中,输入:
GDB 会友好地给你展示成这样:eflags 0x246 [ PF ZF IF ]
- 方括号里出现的名字,说明该标志位当前为 1。
- 没出现的名字,说明当前为 0。
六、 总结:标志位的“性格”
- ZF:它是最常用的。看到
JNE、JE就要立刻想到它。 - CF vs OF:
- 如果你把数字当成没有正负之分的纯二进制(无符号),看 CF。
- 如果你把数字当成有正负之分的补码(有符号),看 OF。
- 标志寄存器是自动的:你不需要用
MOV去写它,你只需要通过CMP或TEST命令“暗示” CPU 去修改它,然后用Jcc指令去利用它。
练习建议:
尝试修改你之前的 test.asm 代码,在 mov 后面加一行 cmp eax, 0,然后在 GDB 里单步执行,观察执行完 cmp 后,eflags 里的 ZF 是否出现了!
https://i.sstatic.net/aPbbT.png