【问题标题】:Can an ADD set both Carry and Overflow flags?ADD 可以同时设置进位和溢出标志吗?
【发布时间】:2018-06-19 07:20:37
【问题描述】:

我已经从事装配工作几个月了。我知道进位标志和溢出标志的区别。

我的问题(我在谷歌上找不到答案)是是否可以同时激活进位和溢出标志。我不知道我在哪里听说只能激活一个标志(或进位或溢出)(不能同时激活)。

假设我们有下一个代码:

xor eax, eax
xor ebx, ebx
mov al, 128
mov bl, 128
add al, bl

最后一行是否激活了 C 和 O 标志? (注意 al 和 bl 符号相同)

在上述情况下,我会说只有进位会被激活。我错了吗?

【问题讨论】:

  • 请不要用不相关的语言标记您的问题。这个问题与 C 或 C++ 无关,因此试图吸引 C/C++ 程序员不会给你很好的答案。
  • 您可以自己轻松尝试:单步越过add 并查看FLAGS。或者在add 之后放置一个int3 指令。
  • 这里的正确答案是阅读处理器上的文档。虽然我们这里的大多数人都为 x86 记住了这一点,但通常并非所有指令都触及所有指令集的所有标志(一些 ISA 没有标志),因此习惯于在文档中查找它。并且绝对进位、溢出、零检查和负标志(以及任何其他标志)是单独的逻辑路径,可以评估一个、无、全部或任何组合以进行操作。阅读该处理器/核心指令的文档。
  • 现在取决于您所说的激活,如果标志是操作的一部分,那么在操作非常相关之后它是零还是一,这两种状态都有价值。如果您的问题不是关于是否可以这样做,但在此操作结束时两者都不是零(一),这是一个不同的问题,对于 8 位(或任意数量的位操作),有符号的 0x80+0x80 结果在有符号的溢出中,查看操作 1 + 1 = 10 二进制的 lsbits。进位与进位不同,即操作数相同,msbit结果不同1+1=0。
  • 进位,也就是进位位 AKA 无符号溢出,也被设置,因为 1 + 1 = 10 二进制所以如果这被评估为该指令集所做的 8 位,并且是 8 bit 在位 7/8 边界上计算两者,那么是的,您应该看到两个溢出,无符号和有符号,设置,非零。

标签: assembly x86 integer-overflow


【解决方案1】:

添加 127 或更少不能同时设置 OF 和 CF(对于任何起始值)。也许您读到的内容是关于添加1? (注意 inc / dec 保持 CF 不变,所以这只适用于 add al, 1

顺便说一句,如果您使用来自 0..255 的嵌套循环进行搜索,则 128 + 128 是设置两个标志(对于 8 位操作数大小)的第一对输入。我写了一个程序来做到这一点。

global _start
_start:

    mov al, 128
    add al, 128        ; set a breakpoint here and single step this, then look at flags

    xor ecx, ecx
    xor edx, edx

.edx:                       ; do {

.ecx:                       ;   do {
    movzx eax, cl           ; eax as a scratch register every iteration
                      ; writing to eax instead of al avoids a false dependency for performance.
                      ; mov eax, ecx is just as good on AMD and Haswell/Skylake, but would have partial-reg penalties on earlier Intel CPUs.
    add   al, dl
    seto  al                ; al = OF  (0 or 1)
    lahf                    ; CF is the lowest bit of FLAGS.  LAFH loads AH from the low byte of FLAGS.
    test  ah, al            ; ZF = OF & CF
    jnz   .carry_and_overflow

.continue:
    add   cl, 1             ;    add to set CF on unsigned wraparound
    jnc   .ecx              ;    } while(cl++ doesn't wrap)
    ; fall through when ECX=0

    add   dl, 1
    jnc   .edx              ; } while(dl++ doesn't wrap)

    xor   edi,edi
    mov   eax, 231
    syscall          ; exit(0) Linux 64-bit.

.carry_and_overflow:
    int3             ; run this code inside GDB.
                     ; int3 is a software breakpoint
                     ; if execution stops here, look at cl and dl
    jmp  .continue

使用 NASM 或 YASM 在 Linux(或任何操作系统,如果您在退出系统调用之前的断点处停止)上构建。

yasm -felf64 -Worphan-labels -gdwarf2 foo.asm &&
ld -o foo foo.o

我在 gdb 下使用gdb ./foo 运行此程序,然后使用run

在我的~/.gdbinit 我有:

set disassembly-flavor intel
layout reg

set print static-members off
set print pretty on
macro define offsetof(t, f) &((t *) 0)->f)  # https://stackoverflow.com/questions/1768620/how-do-i-show-what-fields-a-struct-has-in-gdb#comment78715348_1770422

layout reg 将 GDB 置于文本 UI 全屏模式(而不是面向行)。在第一个c(继续)命令(rax=128/rdx=128)之后按住 return 一段时间,然后按 control-L 重新绘制屏幕,​​因为 GDB 的 TUI 东西不能很好地工作,我得到了这个:

┌──Register group: general──────────────────────────────────────────────────────────────────────────────────────────────────────┐
│rax            0x301    769                                    rbx            0x0      0                                       │
│rcx            0xa7     167                                    rdx            0x85     133                                     │
│rsi            0x0      0                                      rdi            0x0      0                                       │
│rbp            0x0      0x0                                    rsp            0x7fffffffe6c0   0x7fffffffe6c0                  │
│r8             0x0      0                                      r9             0x0      0                                       │
│r10            0x0      0                                      r11            0x0      0                                       │
│r12            0x0      0                                      r13            0x0      0                                       │
│r14            0x0      0                                      r15            0x0      0                                       │
│rip            0x4000a5 0x4000a5 <_start.carry_and_overflow+1> eflags         0x202    [ IF ]                                  │
│cs             0x33     51                                     ss             0x2b     43                                      │
│ds             0x0      0                                      es             0x0      0                                       │
│fs             0x0      0                                      gs             0x0      0                                       │
│                                                                                                                               │
   ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
   │0x400089 <_start.edx+5>                 seto   al                                                                           │
   │0x40008c <_start.edx+8>                 lahf                                                                                │
   │0x40008d <_start.edx+9>                 test   ah,al                                                                        │
   │0x40008f <_start.edx+11>                jne    0x4000a4 <_start.carry_and_overflow>                                         │
   │0x400091 <_start.continue>              add    cl,0x1                                                                       │
   │0x400094 <_start.continue+3>            jae    0x400084 <_start.edx>                                                        │
   │0x400096 <_start.continue+5>            add    dl,0x1                                                                       │
   │0x400099 <_start.continue+8>            jae    0x400084 <_start.edx>                                                        │
   │0x40009b <_start.continue+10>           xor    edi,edi                                                                      │
   │0x40009d <_start.continue+12>           mov    eax,0xe7                                                                     │
   │0x4000a2 <_start.continue+17>           syscall                                                                             │
   │0x4000a4 <_start.carry_and_overflow>    int3                                                                                │
  >│0x4000a5 <_start.carry_and_overflow+1>  jmp    0x400091 <_start.continue>                                                   │
   └────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
native process 5074 In: _start.carry_and_overflow                                                             L37   PC: 0x4000a5 
Program received signal SIGTRAP, Trace/breakpoint trap.
_start.carry_and_overflow () at foo.asm:37
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
_start.carry_and_overflow () at foo.asm:37
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
_start.carry_and_overflow () at foo.asm:37
Continuing.

Program received signal SIGTRAP, Trace/breakpoint trap.
_start.carry_and_overflow () at foo.asm:37
(gdb) 

这个模式很有趣,但一旦你停下来思考数学就很容易解释:对于 DL=128,从 128 到 255 的所有 CL 值同时设置 CF 和 OF。但是对于更高的 DL 值,只有 CL 从 128 到小于 255 的某个值同时设置。因为例如 133 代表 133 - 256 = -123 和 (-123) + (-5) = -128,没有签名的溢出。非常大的无符号值表示 -1 或以下的有符号值。


另见:

【讨论】:

    【解决方案2】:

    您可以将此结果推广到同时设置 C 和 O 的确切条件。加法中 C 和 O 的规则是(至少这是一种可能的表述)

    • C = 高位进位
    • O = 最高位进位 XOR 最高位进位

    因此它们都可以为真,即恰好在最高位有进位但没有进位时。

    【讨论】:

      【解决方案3】:

      是的,在你给出的例子中,进位和溢出都会被设置。

      溢出标志与有符号数有关。您的示例是添加-128 + -128。结果 (-256) 显然不适合 8 位寄存器,因此设置了溢出标志。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-02-05
        • 2016-08-03
        • 1970-01-01
        • 2013-10-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多