【问题标题】:How cmp assembly instruction sets flags (X86_64 GNU Linux)cmp 汇编指令如何设置标志(X86_64 GNU Linux)
【发布时间】:2017-10-06 01:46:25
【问题描述】:

这是一个简单的 C 程序:

void main()
{
       unsigned char number1 = 4;
       unsigned char number2 = 5;

       if (number1 < number2)
       {
               number1 = 0;
       }
}

所以我们在这里比较两个数字。在汇编中,它将使用 cmp 完成。 cmp 通过从另一个操作数中减去一个操作数来工作。

现在 cmp 如何减去操作数?它是从第二个操作数中减去第一个操作数,反之亦然?无论如何,这应该是这样的:

案例#1:

4 - 5 = (0000 0100 - 0000 0101) = (0000 0100 + 1111 1010 + 1) = (0000 0100 + 1111 1011)

= 1111 1111 = -1

所以由于符号位 = 1,所以 SF 应该是 1。

没有进位,所以 CF 应该 = 0。

案例#2:

5 - 4 = (0000 0101 - 0000 0100) = (0000 0101 + 1111 1011 + 1)

= (0000 0101 + 1111 1100) = 1 0000 0001

所以这里,CF 应该是 = 1

由于结果为正,SF 应该 = 0

现在我编译并运行程序(linux x86_64,gcc,gdb),在cmp指令后放置一个断点来查看寄存器状态。

cmp 后断点命中:

Breakpoint 2, 0x0000000000400509 in main ()
(gdb) disassemble
Dump of assembler code for function main:
   0x00000000004004f6 <+0>:     push   %rbp
   0x00000000004004f7 <+1>:     mov    %rsp,%rbp
   0x00000000004004fa <+4>:     movb   $0x4,-0x2(%rbp)
   0x00000000004004fe <+8>:     movb   $0x5,-0x1(%rbp)
   0x0000000000400502 <+12>:    movzbl -0x2(%rbp),%eax
   0x0000000000400506 <+16>:    cmp    -0x1(%rbp),%al
=> 0x0000000000400509 <+19>:    jae    0x40050f <main+25>
   0x000000000040050b <+21>:    movb   $0x0,-0x2(%rbp)
   0x000000000040050f <+25>:    pop    %rbp
   0x0000000000400510 <+26>:    retq
End of assembler dump.

在执行 cmp 后注册转储:

(gdb) info reg
rax            0x4  4
rbx            0x0  0
rcx            0x0  0
rdx            0x7fffffffe608   140737488348680
rsi            0x7fffffffe5f8   140737488348664
rdi            0x1  1
rbp            0x7fffffffe510   0x7fffffffe510
rsp            0x7fffffffe510   0x7fffffffe510
r8             0x7ffff7dd4dd0   140737351863760
r9             0x7ffff7de99d0   140737351948752
r10            0x833    2099
r11            0x7ffff7a2f950   140737348041040
r12            0x400400 4195328
r13            0x7fffffffe5f0   140737488348656
r14            0x0  0
r15            0x0  0
rip            0x400509 0x400509 <main+19>
eflags         0x297    [ CF PF AF SF IF ]
cs             0x33 51
ss             0x2b 43
ds             0x0  0
es             0x0  0
fs             0x0  0
gs             0x0  0
(gdb)

所以我们可以看到,在执行完 cmp 之后,CF=1,SF=1。

所以实际产生的标志 (CF=1 & SF=1) 不等于我们在

中计算的标志

案例#1(CF=0 & SF=1)或案例#2(CF=1 & SF=0)

然后呢? cmp 实际上是如何设置标志的?

【问题讨论】:

  • 您误解了 CF 的值...是什么让您认为 4-5 是“无进位”? (反之亦然),这是完全错误的!
  • 这个问题展示了很好的研究工作! +1
  • @Tommylee2k 是的,我做错了。感谢您指出这一点。

标签: assembly x86-64 att


【解决方案1】:

CMP 的运作
CMP 执行减法但不存储结果。
出于这个原因,对标志的影响在以下之间完全相同:

cmp eax,ecx
sub eax,ecx

根据documentation

操作
temp ← SRC1 − SignExtend(SRC2);
修改状态标志; (* 以与 SUB 指令相同的方式修改状态标志*)
受影响的标志
根据结果​​设置CF、OF、SF、ZF、AF、PF标志。

对标志的影响
所以以下标志会受到这样的影响:

Assume result = op1 - op2

CF - 1 if unsigned op2 > unsigned op1
OF - 1 if sign bit of OP1 != sign bit of result
SF - 1 if MSB (aka sign bit) of result = 1
ZF - 1 if Result = 0 (i.e. op1=op2)
AF - 1 if Carry in the low nibble of result
PF - 1 if Parity of Least significant byte is even

我建议你在这里阅读 OF 和 CF:http://teaching.idallen.com/dat2343/10f/notes/040_overflow.txt

操作数的顺序
我看到你喜欢痛苦,并且正在使用 x86 程序集的 Braindead 变体,称为 ATT-syntax。
在这种情况下,您需要考虑到

CMP %EAX, %ECX  =>  result for the flags = ECX - EAX
CMP OP2, OP1    =   flags = OP1 - OP2

而英特尔语法是

CMP ECX, EAX    =>  result for the flags = ECX - EAX
CMP OP1, OP2    =>  flags = OP1 - OP2

您可以使用以下命令指示 gdb 向您展示 Intel 语法:set disassembly-flavor intel

【讨论】:

    【解决方案2】:

    我想我现在明白了。这就是我认为的方式(设置了借用标志)

    4 - 5
    
    1st operand = 4 = 0000 0100
    2nd operand = 5 = 0000 0101
    
    So we have to perform
    
          1st operand
        - 2nd operand
        --------------
    
    
          7654 3210 <-- Bit number
          0000 0100
        - 0000 0101
        ------------
    
    Lets start.
    
    Bit 0 of 1st operand = 0
    Bit 0 of 2nd operand = 1
    
    so
    
      0
    - 1 
     ===
      ?
    

    这样做,

    让我们从第一个操作数的第 0 位左侧借一个 1。

    所以我们看到第一个操作数的第 2 位是 1。

    当位 2 = 1 时,表示 4。

    我们知道我们可以把 4 写成 2 + 2。所以我们可以把 4 写成两个 2。

          7654 3210 <-- Bit number
                 1
                 1         
          0000 0000
        - 0000 0101
        ------------
    

    所以在上述步骤中,我们将第一个操作数的第 4 位写为两个 2(第一个操作数的第 2 位顶部的两个 1。)

    现在我们知道,一个 2 可以写成两个 1。 所以我们从第一个操作数的第 1 位借一个 1,并在第一个操作数的第 0 位写入两个 1。

          7654 3210 <-- Bit number
                  1
                 11         
          0000 0000
        - 0000 0101
        ------------
    

    现在我们准备对位 0 和位 1 执行减法。

          7654 3210 <-- Bit number
                  1
                 11         
          0000 0000
        - 0000 0101
        ------------
                 11
    

    所以在解决了位 0 和位 1 之后,让我们看看位 2。

    我们再次看到同样的问题。

    第一个操作数的位 2 = 0

    第二个操作数的位 2 = 1

    为此,让我们从第一个操作数的第 2 位左侧借一个 1。

        8 7654 3210 <-- Bit number
                  1
                 11         
        1 0000 0000
        - 0000 0101
        ------------
                 11
    

    现在你看,第一个操作数的第 8 位是 1。我们借用了这个 1。

    在这个阶段,进位标志将被设置。所以 CF=1。

    现在,如果第 8 位为 1,则表示 256。

    256 = 128 + 128

    如果第7位为1,则表示128。我们可以重写为

        8 7654 3210 <-- Bit number
          1       1
          1      11         
          0000 0000
        - 0000 0101
        ------------
                 11
    

    和之前一样,我们可以重写为:

        8 7654 3210 <-- Bit number
           1      1
          11     11         
          0000 0000
        - 0000 0101
        ------------
                 11
    

    和之前一样,我们可以重写为:

        8 7654 3210 <-- Bit number
            1     1
          111    11         
          0000 0000
        - 0000 0101
        ------------
                 11
    

    和之前一样,我们可以重写为:

        8 7654 3210 <-- Bit number
             1    1
          1111   11         
          0000 0000
        - 0000 0101
        ------------
                 11
    

    和之前一样,我们可以重写为:

        8 7654 3210 <-- Bit number
               1  1
          1111 1 11         
          0000 0000
        - 0000 0101
        ------------
                 11
    

    和之前一样,我们可以重写为:

        8 7654 3210 <-- Bit number
                1 1
          1111 1111         
          0000 0000
        - 0000 0101
        ------------
                 11
    

    我们终于可以解决这个问题了。

    从上面减去第二个操作数将得到

        8 7654 3210 <-- Bit number
                1 1
          1111 1111         
          0000 0000
        - 0000 0101
        ------------
          1111 1111
    
    
    So result = 1111 1111
    

    注意,结果中的符号位 = 位 7 = 1

    所以标志标志将被设置。即 SF=1

    因此在 4 - 5 之后 SF=1, CF=1

    【讨论】:

    • 您的文字图片isn't very helpful。它无法大声朗读或复制到编辑器中,并且索引不是很好,这意味着有相同问题的其他用户不太可能在这里找到答案。请edit您的帖子直接合并相关文字。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-12-29
    • 1970-01-01
    • 2014-02-28
    • 2014-06-19
    相关资源
    最近更新 更多