スタックフレーム続き

とりあえず gcc -S してみた。
まずはmain部をretするまで。

main:
        leal    4(%esp), %ecx
        andl    $-16, %esp
        pushl   -4(%ecx)
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ecx
        subl    $20, %esp
        movl    $printf, 8(%esp)
        movl    $main, 4(%esp)
        movl    $.LC1, (%esp)
        call    printf
        movl    $4, 12(%esp)
        movl    $3, 8(%esp)
        movl    $2, 4(%esp)
        movl    $1, (%esp)
        call    func
        addl    $20, %esp
        popl    %ecx
        popl    %ebp
        leal    -4(%ecx), %esp
        ret

呼び出される側の、printfのための準備の直前までとprintf呼出後retまで。

func:
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %edi
        pushl   %esi
        pushl   %ebx
        subl    $124, %esp
# 中略
        call    printf
        movl    -20(%ebp), %eax
        addl    $124, %esp
        popl    %ebx
        popl    %esi
        popl    %edi
        popl    %ebp
        ret

いくつか興味深いことが分かる。*1

  • 関数を呼ぶ前にスタックポインタは次のスタック位置まで延ばしてある。subl $20 %espがそれ
    • 呼んだあとは addl $20 %esp と元に戻してる。お行儀良い
      • 二回連続で呼ぶときのスタックポインタの動きは一回分で済ませてる
  • よくわからなかった値の部分は、どうやら%edi、%esi、%ebxという名の何からしい
    • %edi、%esiはデータ転送命令に使われるアドレスらしい
    • %ebxはアドレスを保持するレジスタらしい
  • 現在のアドレスを入れる命令は書かれていない
    • 恐らくcallすると現在のpcがpushされるのではないかと

しかしまあ、スタックってのは良くできてますね。たったこれだけの作業で関数手続き(どっちだよ)を作ってしまうんだからびっくりだ。


とりあえず、%ebxって戻り値じゃね?と思って実験してみたけど、どうやら全然違うらしい。やっぱよくわからないな、これ。


戻り値を使う場合のfuncの後半部分を載せるよ。

        call    printf
        movl    -16(%ebp), %eax
        addl    $92, %esp
        popl    %ebx
        popl    %esi
        popl    %edi
        popl    %ebp
        ret

これによると、戻り値を格納する場合は %eax に格納しているみたい。
むむ。

*1:環境:Ubuntu 7.04 Feisty Fawn、gcc 4.1.2