EFLAGS寄存器中状态标志(Status Flags)小结

改变EIP/RIP的指令有哪些?

根据INTEL手册可以得出结论,程序中我们不能直接修改EIP寄存器的值,即不允许我们使用类似MOV指令直接给EIP赋值,只能通过其他指令隐式地(implicitly)修改其值。这些指令包括JMP,Jcc,CALL,RET和IRET,还有CPU的中断和异常其实也是改变了EIP的值。

其中JMP,CALL,RET/IRET,中断和异常我们可以成为无条件分支(Unconditional Branches),而Jcc (jump on condition code cc)称为有条件分支(Conditional Branches)。

本文关注有条件分支。有条件分支是根据什么来决定跳转的呢?那就是EFLAGS寄存器中的状态标志(Status Flags)。

关于EFLAG寄存器,它是X86 CPU的状态寄存器,它的重要性不言而喻

EFLAGS寄存器中状态标志(Status Flags)小结_第1张图片

INTEL手册将EFLAGS中的标志按功能划分为三类,分别为状态标志(status flags)、控制标志(control flags)和系统标志(system flags)。我们这里只关注状态标志,即哪些指令会改变状态标志,然后Jcc指令又是如何根据状态标志决定程序执行流程的。


CPU只认识二进制数据,不知道符号

在继续之前,我们要统一的一个问题就是,CPU并不知道一个数字的符号。那平时编程中,我们的有符号数和无符号数是给谁看的呢? 答案是给编译器看的,编译器负责将这些有符号数据和无符号数据转换成正确的二进制数据序列(补码形式),最后到了CPU执行时,这些数据就仅仅是数据了,并没有符号之分。我们从内存中获取的数据,究竟是有符号还是无符号,依然是靠编译器为我们区分,说到底是我们人自己去区分,所以编程语言中才会有signed和unsigned之分。

例如,在C语言中,int a = -1;和unsigned int b = 0xffffffff;生成的汇编代码中数据是一样的

5               int a = -1;
=> 0x080483e1 <+6>:     c7 45 f8 ff ff ff ff    movl   $0xffffffff,-0x8(%ebp)

6               unsigned int b = 0xffffffff;
   0x080483e8 <+13>:    c7 45 fc ff ff ff ff    movl   $0xffffffff,-0x4(%ebp)

看到没,编译器已经帮我们将-1转换成正确的补码形式,最终在CPU眼中,a和b都是32个1的比特流,并没有什么不同,最终解释权归程序员所有,哈哈!是不是感觉CPU很low呢?

那么问题来了,既然CPU不知道自己操作的到底是无符号数还是有符号数,它只管进行二进制运算,那么我们(程序员)要根据什么来判断计算的结果有没有问题呢?请看下面一个例子:



EFLAGS中的状态标志

EFLAGS的状态标志代表什么意思呢?它们代表的是算数指令(arithmetic instruction)的结果状态,算数指令就是大家熟悉的的加减乘除,ADD,SUB,MUL和DIV,当然还有很多其他指令暗含有这些基本的算数指令,比如cmp指令就暗含有sub操作,因此cmp也会影响状态标志。

那么算数指令会产生哪些不同的结果呢?这个比较恼人。

我们先来看看EFLAGS提供了哪些状态标识位,从INTEL手册上了解到,EFLAGS的第0,2,4,6,7和11位(bit)是状态标志位。分别为:

CF(bit 0) -- 进位标志位

PF(bit 2) -- 奇偶校验位

AF(bit 4) -- 辅助进位标志位

ZF(bit 6) -- 零标志位

SF(bit 7) -- 符号标志位

OF(bit 11) -- 溢出标志位

是不是开始晕了! 我相信绝大多数人跟我一样,看到这些都是晕头转向的,傻傻搞不清楚每个具体什么含义!

具体的疑惑大致有两点:

(1)算数指令在什么计算结果下会设置以上提到的6种状态标志?

(2)Jcc指令又是根据什么标志位来决定程序控制流的?

接下来我们就通过具体的实例尽量搞清楚它们吧!


CF 进位标志位

CF是Carry Flag的缩写,即进位标志位。等等,到底什么是进位? 可能你觉得怎么会问这么幼稚的问题?反正我是不知道什么是进位!

现给出进位的定义:



参考链接:

《x86—EFLAGS寄存器详解 》

《EFLAGS标志寄存器加深理解》

《我和CPU之间关于EFLAGS的对话》

《溢出,符号与进位》


你可能感兴趣的:(汇编语言,OS原理)