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的状态寄存器,它的重要性不言而喻

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的缩写,即进位标志位。等等,到底什么是进位? 可能你觉得怎么会问这么幼稚的问题?反正我是不知道什么是进位!

现给出进位的定义:

参考链接:

The CARRY flag and OVERFLOW flag in binary arithmetic

x86—EFLAGS寄存器详解

EFLAGS标志寄存器加深理解

我和CPU之间关于EFLAGS的对话

溢出,符号与进位

  • 6
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
汇编语言寄存器是用来存储和处理数据的。x86 架构的 CPU 有多个寄存器,每个寄存器都有特定的用途。下面是常用的 x86 架构寄存器及其作用: 1. 通用寄存器(General-purpose registers): - EAX:累加器(accumulator),用于算术和逻辑运算。 - EBX:基址寄存器(base register),通常用于存放数据的指针。 - ECX:计数器(counter),用于循环和字符串操作。 - EDX:数据寄存器(data register),用于算术操作和存放 I/O 端口地址。 这些寄存器的 16 位版本分别为 AX、BX、CX、DX。 2. 段寄存器(Segment registers): - CS:代码段寄存器(code segment),存放代码的段地址。 - DS:数据段寄存器(data segment),存放数据的段地址。 - SS:堆栈段寄存器(stack segment),存放堆栈的段地址。 - ES:附加段寄存器(extra segment),通常用于字符串操作。 3. 指针寄存器(Pointer registers): - ESP:堆栈指针寄存器(stack pointer),存放栈顶地址。 - EBP:基址指针寄存器(base pointer),用于存放堆栈帧的基地址。 4. 索引寄存器(Index registers): - ESI:源索引寄存器(source index),通常用于字符串操作。 - EDI:目的索引寄存器(destination index),通常用于字符串操作。 这些寄存器的 16 位版本分别为 SI 和 DI。 除了以上列出的寄存器,还有一些特殊用途的寄存器,如 EFLAGS 寄存器用于存储 CPU 的状态标志位,CR 寄存器用于控制 CPU 的行为等。每个 CPU 架构的寄存器都有其特定的用途和规定操作,需要根据具体的架构和指令集手册来进行学习和使用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值