アセンブリメモ -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
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
続いて, 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
次に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
注目すべき点は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>