Binary2.0勉強会 7
二週間も間があきました。びっくり!
夏バテです。より正確には熱くてバテてるというより冷房が寒くて凍えてます。
関数を呼び出した際のスタックの動きをおさらい。
呼んだ時。
帰る時。
でした。ということで main を見直して見ましょう。
int main(){ 0: 8d 4c 24 04 lea 0x4(%esp),%ecx 4: 83 e4 f0 and $0xfffffff0,%esp 7: ff 71 fc pushl -0x4(%ecx) a: 55 push %ebp b: 89 e5 mov %esp,%ebp d: 51 push %ecx return 0; e: b8 00 00 00 00 mov $0x0,%eax } 13: 59 pop %ecx 14: 5d pop %ebp 15: 8d 61 fc lea -0x4(%ecx),%esp 18: c3 ret
このうち、
a: 55 push %ebp b: 89 e5 mov %esp,%ebp 14: 5d pop %ebp
この三行が上にまとめた部分に相当するわけですが、何か他にも色々してますね。
まず、
4: 83 e4 f0 and $0xfffffff0,%esp
これは esp を 16byte でアラインメント整えてますね。
アラインメント整えると値が変わってしまうので、 main 関数抜ける時には esp を元の値に戻さないといけません。どうやら ecx を使って復元しているようなのですが…何かアドレスをわざわざ +4 してますね。なんでだろー。
と思ってたのですが、よくよく考えれば main も普通に引数取るじゃん引数じゃんということに気がついた。
00000000 <main>: int main(int argc, char *argv[]){ 0: 8d 4c 24 04 lea 0x4(%esp),%ecx 4: 83 e4 f0 and $0xfffffff0,%esp 7: ff 71 fc pushl -0x4(%ecx) a: 55 push %ebp b: 89 e5 mov %esp,%ebp d: 51 push %ecx return argc; e: 8b 01 mov (%ecx),%eax } 10: 59 pop %ecx 11: 5d pop %ebp 12: 8d 61 fc lea -0x4(%ecx),%esp 15: c3 ret
なるほど ecx は普通に第一引数指すのに使っているようです。
ということは ecx は引数のアドレスを指しつつ、ついでにその値を esp の復元にも利用している、ような感じなのかな、と。
で、これそもそも push と pop の対応が余裕で取れてないですね。なんでやねん。 esp 復元してるんで esp がずれるといった問題はないんですが、一番最初の「pushl -0x4(%ecx)」の意味が全然ないような…本来なら最後の「lea -0x4(%ecx),%esp」は「pop %esp」とされるべきだと思うのですが、どうなんだろう。スタック触るよりレジスタ間で値渡した方が早いからとか?それなら push そもそもするなよ…これだけ結局分かりませんでした。誰か偉いひと教えてください。最適化オプションつけても消えないということは必要なんだとは思うのですが…
ということで随分とかかりましたが、 0 返すだけの main をようやくアセンブラレベルで理解できましたね!わからないところあるじゃんとか言わない!
次回からは配列とか構造体とかほげろうと思います。