アセンブリメモ -if文-

Cを逆アセンブルした結果どのようになるかのメモ.
まずはif文

if文

   #include <stdio.h>

   int main(){
      int x = 1;
      if(x > 5){
         printf("x > 5\n");
      }
      return 0;
   }
   0x080483fb <+0>:  lea    0x4(%esp),%ecx
   0x080483ff <+4>:  and    $0xfffffff0,%esp
   0x08048402 <+7>:  pushl  -0x4(%ecx)
   0x08048405 <+10>: push   %ebp
   0x08048406 <+11>: mov    %esp,%ebp
   0x08048408 <+13>: push   %ecx
   0x08048409 <+14>: sub    $0x14,%esp
   0x0804840c <+17>: movl   $0x1,-0xc(%ebp)
   0x08048413 <+24>: cmpl   $0x5,-0xc(%ebp)
   0x08048417 <+28>: jle    0x8048429 <main+46>
   0x08048419 <+30>: sub    $0xc,%esp
   0x0804841c <+33>: push   $0x80484d0
   0x08048421 <+38>: call   0x80482d0 <puts@plt>
   0x08048426 <+43>: add    $0x10,%esp
   0x08048429 <+46>: mov    $0x0,%eax
   0x0804842e <+51>: mov    -0x4(%ebp),%ecx
   0x08048431 <+54>: leave  
   0x08048432 <+55>: lea    -0x4(%ecx),%esp
   0x08048435 <+58>: ret    

f:id:yagamikou:20150924012430p:plain

0x0804840cにて変数x(%ebp-0xc)に1を代入している.
0x08048413にて変数xと0x5を比較している.
0x08048417にてjle命令で0x8048429へと処理が移る.
jle命令は"より小さいか等しい"という条件である.
つまり, 変数xが5より小さい場合に0x8048429へと処理が移るという意味である.
そのため, if文の条件によってcmpの次の命令が変わる.

if文の条件をx < 5とすると次のようになる.

   0x0804840c <+17>: movl   $0x1,-0xc(%ebp)
   0x08048413 <+24>: cmpl   $0x4,-0xc(%ebp)
   0x08048417 <+28>: jg     0x8048429 <main+46>

jg命令は"より大きい"である.
つまり, xが4より大きいならば0x8048429へ処理が移る.

また, x == 5とすると次のようになる.

   0x0804840c <+17>: movl   $0x1,-0xc(%ebp)
   0x08048413 <+24>: cmpl   $0x5,-0xc(%ebp)
   0x08048417 <+28>: jne    0x8048429 <main+46>

jneは"等しくない時に"という意味である.
つまりxと5が等しくない時に0x8048429に処理が移る.

次にif-elseの場合.

   #include <stdio.h>

   int main(){
      int x = 1;
      if(x == 5){
         printf("x == 5\n");
      } else {
         printf("x != 5\n");
      }
      return 0;
   }
   0x080483fb <+0>:  lea    0x4(%esp),%ecx
   0x080483ff <+4>:  and    $0xfffffff0,%esp
   0x08048402 <+7>:  pushl  -0x4(%ecx)
   0x08048405 <+10>: push   %ebp
   0x08048406 <+11>: mov    %esp,%ebp
   0x08048408 <+13>: push   %ecx
   0x08048409 <+14>: sub    $0x14,%esp
   0x0804840c <+17>: movl   $0x1,-0xc(%ebp)
   0x08048413 <+24>: cmpl   $0x5,-0xc(%ebp)
   0x08048417 <+28>: jne    0x804842b <main+48>
   0x08048419 <+30>: sub    $0xc,%esp
   0x0804841c <+33>: push   $0x80484e0
   0x08048421 <+38>: call   0x80482d0 <puts@plt>
   0x08048426 <+43>: add    $0x10,%esp
   0x08048429 <+46>: jmp    0x804843b <main+64>
   0x0804842b <+48>: sub    $0xc,%esp
   0x0804842e <+51>: push   $0x80484e7
   0x08048433 <+56>: call   0x80482d0 <puts@plt>
   0x08048438 <+61>: add    $0x10,%esp
   0x0804843b <+64>: mov    $0x0,%eax
   0x08048440 <+69>: mov    -0x4(%ebp),%ecx
   0x08048443 <+72>: leave  
   0x08048444 <+73>: lea    -0x4(%ecx),%esp
   0x08048447 <+76>: ret    

f:id:yagamikou:20150924012512p:plain

続いて, else ifを追加した場合.

   #include <stdio.h>

   int main(){
      int x = 5;
      if(x > 5){
         printf("x > 5\n");
      } else if( x < 5){
         printf("x < 5\n");
      } else {
         printf("x == 5\n");
      }
      return 0;
   }
   0x080483fb <+0>:  lea    0x4(%esp),%ecx
   0x080483ff <+4>:  and    $0xfffffff0,%esp
   0x08048402 <+7>:  pushl  -0x4(%ecx)
   0x08048405 <+10>: push   %ebp
   0x08048406 <+11>: mov    %esp,%ebp
   0x08048408 <+13>: push   %ecx
   0x08048409 <+14>: sub    $0x14,%esp
   0x0804840c <+17>: movl   $0x5,-0xc(%ebp)
   0x08048413 <+24>: cmpl   $0x5,-0xc(%ebp)
   0x08048417 <+28>: jle    0x804842b <main+48>
   0x08048419 <+30>: sub    $0xc,%esp
   0x0804841c <+33>: push   $0x80484f0
   0x08048421 <+38>: call   0x80482d0 <puts@plt>
   0x08048426 <+43>: add    $0x10,%esp
   0x08048429 <+46>: jmp    0x8048453 <main+88>
   0x0804842b <+48>: cmpl   $0x4,-0xc(%ebp)
   0x0804842f <+52>: jg     0x8048443 <main+72>
   0x08048431 <+54>: sub    $0xc,%esp
   0x08048434 <+57>: push   $0x80484f6
   0x08048439 <+62>: call   0x80482d0 <puts@plt>
   0x0804843e <+67>: add    $0x10,%esp
   0x08048441 <+70>: jmp    0x8048453 <main+88>
   0x08048443 <+72>: sub    $0xc,%esp
   0x08048446 <+75>: push   $0x80484fc
   0x0804844b <+80>: call   0x80482d0 <puts@plt>
   0x08048450 <+85>: add    $0x10,%esp
   0x08048453 <+88>: mov    $0x0,%eax
   0x08048458 <+93>: mov    -0x4(%ebp),%ecx
   0x0804845b <+96>: leave  
   0x0804845c <+97>: lea    -0x4(%ecx),%esp
   0x0804845f <+100>:   ret    

f:id:yagamikou:20150924012524p:plain

次にAND, OR演算子を使用した場合にどういったアセンブリコードになるかを確認する.
まずはAND演算子を用いたコードから.

   #include <stdio.h>

   int main(){
      int x = 1;
      if(x > 0 && x < 10){
         printf("x > 0 && x < 10\n");
      } else {
         printf("x > 10 || x < 0\n");
      }
      return 0;
   }
   0x080483fb <+0>:  lea    0x4(%esp),%ecx
   0x080483ff <+4>:  and    $0xfffffff0,%esp
   0x08048402 <+7>:  pushl  -0x4(%ecx)
   0x08048405 <+10>: push   %ebp
   0x08048406 <+11>: mov    %esp,%ebp
   0x08048408 <+13>: push   %ecx
   0x08048409 <+14>: sub    $0x14,%esp
   0x0804840c <+17>: movl   $0x1,-0xc(%ebp)
   0x08048413 <+24>: cmpl   $0x0,-0xc(%ebp)
   0x08048417 <+28>: jle    0x8048431 <main+54>
   0x08048419 <+30>: cmpl   $0x9,-0xc(%ebp)
   0x0804841d <+34>: jg     0x8048431 <main+54>
   0x0804841f <+36>: sub    $0xc,%esp
   0x08048422 <+39>: push   $0x80484e0
   0x08048427 <+44>: call   0x80482d0 <puts@plt>
   0x0804842c <+49>: add    $0x10,%esp
   0x0804842f <+52>: jmp    0x8048441 <main+70>
   0x08048431 <+54>: sub    $0xc,%esp
   0x08048434 <+57>: push   $0x80484f0
   0x08048439 <+62>: call   0x80482d0 <puts@plt>
   0x0804843e <+67>: add    $0x10,%esp
   0x08048441 <+70>: mov    $0x0,%eax
   0x08048446 <+75>: mov    -0x4(%ebp),%ecx
   0x08048449 <+78>: leave  
   0x0804844a <+79>: lea    -0x4(%ecx),%esp
   0x0804844d <+82>: ret

f:id:yagamikou:20150924012544p:plain

注目すべき点はcmp命令の部分.

   0x08048413 <+24>: cmpl   $0x0,-0xc(%ebp)
   0x08048417 <+28>: jle    0x8048431 <main+54>
   0x08048419 <+30>: cmpl   $0x9,-0xc(%ebp)
   0x0804841d <+34>: jg     0x8048431 <main+54>

条件が1つだったときは

   cmp X, X
   jle 0xXXXXXXX
   call <puts>
   ...
   cmp X, X
   jle 0xXXXXXXX
   cal <puts>
   ...

のように条件分岐後にifステートメント中の処理コード(printfなど)が来ていた.
しかし, AND演算子を用いた結果,

   cmp X, X
   jle 0xXXXXXXXX
   cmp X, X
   jle 0xXXXXXXXX

のようにcmp+jxxのコードが連続して登場するようになる.
OR演算子を使った場合も同様である.

   0x08048413 <+24>: cmpl   $0xa,-0xc(%ebp)
   0x08048417 <+28>: jg     0x804841f <main+36>
   0x08048419 <+30>: cmpl   $0x0,-0xc(%ebp)
   0x0804841d <+34>: jns    0x8048431 <main+54>

f:id:yagamikou:20150924012601p:plain

参考