在16位处理器的时代,标志寄存器是16位的,叫作FLAGS;在32位处理器的时代,或者说,在IA-32架构下,标志寄存器是32位的,叫作EFLAGS;在64位处理器的时代,或者说在x64架构下,标志寄存器扩展到64位,叫作RFLAGS。
在x64架构下,尽管标志寄存器已经扩展到64位,但是高32位是保留不用的,被处理器和软件忽略。
来回顾一下RFLAGS寄存器的低32位,如图2-28所示,从右往左,分别是进位标志CF;奇偶标志PF;辅助进位标志AF;零标志ZF;符号标志SF;陷阱标志TF;中断标志IF;方向标志DF;溢出标志OF;IO特权级IOPL;任务嵌套标志NT;恢复标志RF;虚拟8086模式标志VM;对齐检查标志AC;虚拟中断标志VIF;虚拟中断挂起标志VIP;CPUID指令支持标志ID。
图2-28 标志寄存器的低32位
其中,加框的部分是保留的,包括位1、位3、位5、位15、位22~31。这里只是低32位,刚才我们说了,整个高32位也都是保留的。
下面我们分标志来看。
CF是进位标志,置位表明整数的加减运算,比如add和sub,产生了进位或者借位;加一指令inc或者减一指令dec不影响这个标志位;数位移动指令,比如shl等,将导致移出的比特进入CF标志;逻辑操作指令,比如or、and、xor,清除这个标志;比特测试指令,比如btc等,将会用被测试的那个比特来设置CF标志。
PF是奇偶标志,如果计算结果的最低有效字节中有偶数个1,PF标志被置位;否则硬件将其清零。
AF是辅助进位标志,如果计算结果的位3产生了进位或者借位,则设置此位;否则硬件将其清零。
ZF是零标志,如果最近一次算术操作的结果是0,则设置此标志;否则,硬件将其清零。比较和测试指令也影响此标志。
SF是符号标志,如果最近一次算术操作的结果是负值,则设置此标志;否则,硬件将其清零。
DF是方向标志,用于串操作。软件可将此标志设置为1,以指示在后面的串操作指令执行过程中,数据指针是递减的;如果将此位清零,则数据指针是递增的。
OF是溢出标志,如果最近一次的有符号整数运算导致结果的符号位与两个操作数的符号位不同,则硬件设置此标志;否则,硬件将其清零。逻辑运算指令清除此标志。
TF是陷阱标志,如果软件将此位置1,则软件调试的过程中将允许单步模式;否则将禁止单步模式。如果在一条指令执行前单步模式处于允许状态,则这条指令执行后,将产生一个调试异常#DB,并转入对应的中断处理过程。在任何中断和异常(包括调试异常)发生后,TF标志被自动清零。
IF是中断标志,如果软件将此位置1,则允许处理器响应可屏蔽中断;否则,处理器忽略可屏蔽的中断。对不可屏蔽中断,比如NMI、软中断指令和异常,不受此标志影响。
IOPL是I/O特权级字段,用来指定当前任务或程序的输入输出特权级,也就是指定它可以访问哪些硬件端口。
NT是任务嵌套标志,用于硬件任务切换,指示当前任务是否嵌套在另一个任务中。在保护模式里,很少有软件使用硬件任务切换,IA-32e模式不支持硬件任务切换,所以这一位用处不大。
RF是恢复标志,将其设置为1,可临时禁止指令断点起作用,从而防止重复引发调试异常#DB。调试程序时,设置断点是很常用的操作,断点地址寄存器DR0~DR3用来设定断点地址。如果处理器在执行一条指令前,检测到它的地址符合设定的断点地址,则引发指令断点异常,并转入异常处理过程执行。
我们知道,有些异常属于故障(Faults),有些异常属于陷阱(Traps)。如果是故障,则从中断处理过程返回时,是返回到引起故障的指令;如果是陷阱,则返回到下一条指令。由指令断点引发的调试异常属于故障类型,在指令执行前引发,并在这个异常被处理之后重新执行原来的指令。如果在异常处理程序中没有移除这个指令断点,则指令重新执行时,处理器必将再次检测到这个指令断点,并再次引发调试异常,从而围绕这个指令断点形成一个循环。
为防止发生这种情况,IA-32和x64架构的处理器提供了RF标志位,软件可以根据需要设置该标志,让处理器忽略指令断点。其原理是这样的:
我们知道,在进入中断和异常处理过程时,除了压入CS和指令指针寄存器的内容,还要压入标志寄存器的内容,并在执行中断返回指令时被弹回。平时RF标志为零,在进入异常处理过程时,对指令断点要做特殊处理。如果是指令断点引发的异常,则软件需要修改栈中的RF标志,将其置1。如此一来,当执行中断返回指令并从栈中恢复标志寄存器的内容后,RF是1,就可阻止断点调试异常再次发生。
VM是虚拟8086模式标志,此位只在保护模式下有用。在保护模式下,通过将此位设置为1或者清零,来决定是否允许在保护模式下执行实地址模式的程序。在IA-32e模式下不再支持虚拟8086模式,所以对此位的修改将被忽略。
AC是对齐检查标志,在控制寄存器CR0的AM位是1时,软件将此位置1以允许自动对齐检查。如果此位是1且当前特权级是3,不对齐的内存操作将导致对齐检查异常#AC。
VIF是虚拟中断标志;VIP是虚拟中断挂起标志,这两个标志用于8086虚拟机,所以没有详细了解的必要。
ID是cpuid指令支持标志,如果软件可以修改这一位,表明当前处理器支持cpuid指令。
再来看指令指针寄存器,处理器用它来取指令并加以执行。
在16位处理器上,指令指针寄存器是16位的,叫作IP;在IA-32架构上,指令指针寄存器扩展到32位,叫作EIP;在x64架构上,指令指针寄存器扩展到64位,叫作RIP。
指令指针寄存器的内容不能直接访问和修改,只能通过jmp、call、syscall、sysret等转移指令,或者通过调用门、中断等方式间接改变。
在x64架构上,对指令指针寄存器的使用取决于处理器的工作模式。比如,在实地址模式下只能使用低16位部分IP;再比如,在32位保护模式下只能使用低32位部分EIP。在64位模式下,使用全部的64位RIP。