ちょっとだけアセンブラの基本を勉強しよう。
色々間違ってるかもしれないので、もし通りがかった人が居ましたら指摘等していただけると幸いです。
$ cat main.c int main() { int a = 10; int b = 20; int c = a + b; }
足し算プログラム。これが
$ gcc -m32 main.c -S $ cat main.s .file "main.c" .text .globl main .type main, @function main: pushl %ebp movl %esp, %ebp subl $16, %esp movl $10, -4(%ebp) movl $20, -8(%ebp) movl -8(%ebp), %eax movl -4(%ebp), %edx leal (%edx,%eax), %eax movl %eax, -12(%ebp) leave ret .size main, .-main .ident "GCC: (SUSE Linux) 4.5.1 20101208 [gcc-4_5-branch revision 167585]" .section .comment.SUSE.OPTs,"MS",@progbits,1 .string "ospwg" .section .note.GNU-stack,"",@progbits
こうなった。
「main:」から見ていく。まず1行目のpushlで%ebpレジスタの値をスタックに積んでる(退避?)。続いて、movl(long wordのコピー)命令で%espレジスタの値を%ebpにコピー。%espには現在のスタックのトップのアドレスが格納されているので、%ebpがmain内におけるスタックのベースアドレスになる。
次のsublは減算命令で、%espから16を引く。これ、%ebpをスタックに積んだ分引いたのかと思ったけど、%ebpレジスタって32bit(=4byte)だから計算合わないな・・・後回しにして進めよう。いや、その後の変数3つ積む分のスタックを先に進めてるのか。
4,5行目では、%ebpレジスタの4byte前(スタック的に上)に10(ソース中の変数a)を、8byte前に20(ソース中の変数b)をコピーする。%ebpはスタックのベースを指しているので、スタックに積んでることになる。続いて、今積んだ%ebpの8byte上を%eaxに、4byte上を%edxにコピー。スタックに積んだ意味あったのか・・・と思ったけど、最適化なしでやってるから愚直になるんですね。
(ここで、lea命令が理解できなくて詰まる)
(30分くらい調べた)何となく理解できた。lea自体はアドレスをレジスタに設定する命令だけど、ポイントはアドレスオペランドで、8行目の命令だと「%edxと%eaxがそれぞれ指す先の値の和をとって、そのアドレスを%eaxにコピーする」だと思われる(ホントか?)。そしてその%eaxを%ebpの12byte先にコピーして、最後にleave命令で%ebpの指すアドレスを%espにコピーしてから%ebpをpoplでスタックから取り出して終了。retはmain関数からのreturnだと思う。
主なところを眺めただけでこんなに時間使うとは。もっと色々やりたかったけど、一旦終了。
アセンブラというかGASの本を読みたいな・・・