質問: 9; の部分が movl $0x9,-0x8(%rbp) に相当するのはわかるのですが、 この -0x8 は何ですか?? それと、前後の lea 0x47(%rip),%rdi # 100000fa6 <_main+0x56> movl $0x0,-0x4(%rbp) と mov -0x8(%rbp),%esi mov $0x0,%al この4つは何をしているのでしょうか?? 教えてください。
回答: 昔コンパイラを作っていた経験から、説明します。 このCコンパイラは、コードの最適化を図っているようで、若干、出力されたアセンブラとC言語プログラム行が一致せず、一部が前後しているようです。そこで、元のC言語の順にどのように出力されているか説明します。 前の方が書かれているように 0000000100000f50 <_main>: 100000f50: 55 push %rbp 100000f51: 48 89 e5 mov %rsp,%rbp 100000f54: 48 83 ec 10 sub $0x10,%rsp は、関数に入った時に自動的に出力されるプロローグ処理と呼ばれている部分です。%rbp(ベースポインター)で示されるアドレスから0x10離れた位置に%rsp(スタックポインタ)に設定しています。この間には、関数内で利用する変数などの領域が格納されます。このような変数の取り方を行うことで、再帰呼び出しをしてもそれぞれの関数内で値を保持することができます。通常、スタックポインタは、上下することがあるので、変数を参照する場合には、このベースポインタからの相対位置でアクセスします。 したがって、「int i = 9」は、 100000f66: c7 45 f8 09 00 00 00 movl $0x9,-0x8(%rbp) となります。 次の「printf("%d",i);」は、 100000f58: 48 8d 3d 47 00 00 00 lea 0x47(%rip),%rdi # 100000fa6 <_main+0x56> 100000f6d: 8b 75 f8 mov -0x8(%rbp),%esi 100000f70: b0 00 mov $0x0,%al 100000f72: e8 0d 00 00 00 callq 100000f84 <_main+0x34> 100000f79: 89 45 f4 mov %eax,-0xc(%rbp) となります。 先頭のleaは、”%d”の文字列アドレスを%rdiレジスタ格納するものです。%ripは、命令を実行するメモリ位置を保持するレジスタであり、 0x47(%rip)とは、実行している命令から0x47先であり、具体的には100000fa6 <_main+0x56> となります。"%d"などのようにプログラム中に現れる文字列は変更さることがないので、命令コードが書かれている領域に格納するケースがあります。 次の 100000f6d: 8b 75 f8 mov -0x8(%rbp),%esi で、i変数「-0x8(%rbp)」の値を%esiに格納しています。 100000f70: b0 00 mov $0x0,%al については、ちょっとわかりませんが、可能性があるとするならば、callによって別のサブルーチンを呼び出した後に取得する返却値をあらかじめクリアすることで、サブルーチン側で alレジスタに値を設定していない場合に不正が値が返却されるのを防いでいるのかもしれません。 最後の 100000f79: 89 45 f4 mov %eax,-0xc(%rbp) で、printfからの返却値を「-0xc(%rbp) 」に格納しているようです。 最後に、返却値に0を設定して返却する部分(エピローグ処理)となります。 100000f77: 31 f6 xor %esi,%esi 100000f7c: 89 f0 mov %esi,%eax 100000f7e: 48 83 c4 10 add $0x10,%rsp 100000f82: 5d pop %rbp 100000f83: c3 retq となります。 xor %esi,%esiで%esiレジスタをクリアし、mov %esi,%eaxで、その値を%eaxレジスタに格納しています。直接、%eaxに0をダイレクトに設定して良いのですが、メモリを少なくしたり、高速に動作するために、xor命令を使うケースは多くあります。 最後にスタックポインターをベースポインター位置に戻り、retq命令を実行することで、呼び出し側命令位置に戻ります。 私自身、インテル系の8bitCPUや16bitCPUのアセンブラしか知りませんが、おそらくこのような動作になっているかと思います。