Binary2.0勉強会 6
前回確認した関数の呼び出し回りを実際に逆アセして見てみましょう。
niha@hoge:~/src/c$ cat bin02.c int hoge(int i){ int ret = i; return ret; } niha@hoge:~/src/c$ gcc -c -g bin02.c niha@hoge:~/src/c$ objdump -S bin02.o bin02.o: file format elf32-i386 Disassembly of section .text: 00000000 <hoge>: int hoge(int i){ 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 10 sub $0x10,%esp int ret = i; 6: 8b 45 08 mov 0x8(%ebp),%eax 9: 89 45 fc mov %eax,-0x4(%ebp) return ret; c: 8b 45 fc mov -0x4(%ebp),%eax } f: c9 leave 10: c3 ret
まずは、ベースポインタをスタックに退避させています。
その後、ベースポインタをスタックポインタの値に、スタックポインタは16引いて、16バイト分のスタックが確保されました。16バイトも使わないと思うんですが…なんででしょうね。
ここから関数内の処理。第一引数を EBP 経由でさしているのが「0x8(%ebp)」。ちなみに「0x4(%ebp)」には ret の戻り先が、「(%ebp)」には元のベースポインタが入ってます。
第一引数を EAX に、更に EAX をスタックに積んでいます。そして、返り値としてスタックに積まれた第一引数を EAX レジスタに。
えーと、無駄ですね。無駄です。許せない。
ふざけんな、ということで最適化オプションつけてコンパイルした結果。
0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 8b 45 08 mov 0x8(%ebp),%eax 6: 5d pop %ebp 7: c3 ret
素晴らしー。
あとは leave で EBP と ESP を復元して、 ret でぴょーんと飛んでおしまい。
関数呼ぶときは大体こんな感じで。
niha@hoge:~/src/c$ cat bin03.c int func(int x, int y, int z); void hoge(){ func(1, 2, 3); } niha@hoge:~/src/c$ gcc -c -g bin03.c niha@hoge:~/src/c$ objdump -S bin03.o bin03.o: file format elf32-i386 Disassembly of section .text: 00000000 <hoge>: int func(int x, int y, int z); void hoge(){ 0: 55 push %ebp 1: 89 e5 mov %esp,%ebp 3: 83 ec 18 sub $0x18,%esp func(1, 2, 3); 6: c7 44 24 08 03 00 00 movl $0x3,0x8(%esp) d: 00 e: c7 44 24 04 02 00 00 movl $0x2,0x4(%esp) 15: 00 16: c7 04 24 01 00 00 00 movl $0x1,(%esp) 1d: e8 fc ff ff ff call 1e <hoge+0x1e> } 22: c9 leave 23: c3 ret
スタックにぺぺっと積んで call。おしまい!