web-dev-qa-db-ja.com

cmpアセンブリ命令がフラグを設定する方法(X86_64 GNU Linux)

簡単なCプログラムは次のとおりです。

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

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

したがって、ここでは2つの数値を比較しています。アセンブリでは、cmpを使用して実行されます。 cmpは、一方のオペランドをもう一方のオペランドから減算することによって機能します。

では、cmpはどのようにオペランドを減算していますか? 2番目から1番目のオペランドを引くのですか、それともその逆ですか?いずれにせよ、これは次のようになります。

ケース#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は実際にどのようにフラグを設定していますか?

6
abs

私は今それを理解していると思います。これが私の考えです(借用フラグが設定されています)

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 
 ===
  ?

これをする、

第1オペランドのビット0の左側から1を借りましょう。

したがって、第1オペランドのビット2は1であることがわかります。

ビット2が= 1の場合、それは4を意味します。

4を2+ 2と書くことができるので、4を2つの2と書くことができます。

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

したがって、上記の手順では、第1オペランドのビット4を2つの2として記述しました(第1オペランドのビット2の上に2つの1)。

繰り返しになりますが、2は2つの1として記述できます。したがって、第1オペランドのビット1から1を借用し、第1オペランドのビット0に2つの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を見てみましょう。

再び同じ問題が発生します。

第1オペランドのビット2 = 0

第2オペランドのビット2 = 1

これを行うには、第1オペランドのビット2の左側から1を借用しましょう。

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

ご覧のとおり、第1オペランドのビット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

ついにこれを解決することができます。

上記のすべてから第2オペランドを引くと、

    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

1
abs

CMPの操作
CMPは減算を実行しますが、結果は保存しません。
このため、フラグへの影響は次の間でまったく同じです。

cmp eax,ecx
sub eax,ecx

ドキュメント によると:

操作
temp←SRC1-SignExtend(SRC2);
ModifyStatusFlags; (* 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

オペランドの順序
あなたは痛みが好きで、ATT構文と呼ばれるx86アセンブリのブレインデッドバリアントを使用しているようです。
これはあなたがそれを考慮に入れる必要がある場合です

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

Intel構文は

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

次を使用して、Intel構文を表示するようにgdbに指示できます。 set disassembly-flavor intel