3

次のアセンブリ コードで何が起こっているのかを正確に把握しようとしています。誰かが行ごとに何が起こっているのか説明できますか? 何が起こっていると思うかを入力します(コメントを参照)が、明確にする必要があります。

        .file   "testcalc.c"
        .section        .rodata.str1.1,"aMS",@progbits,1
.LC0:
        .string "x=%d, y=%d, z=%d, result=%d\n"
        .text
.globl main
        .type   main, @function
main:
        leal    4(%esp), %ecx   // establish stack frame
        andl    $-16, %esp      // decrement %esp by 16, align stack
        pushl   -4(%ecx)     // push original stack pointer
        pushl   %ebp     // save base pointer
        movl    %esp, %ebp     // establish stack frame 
        pushl   %ecx         // save to ecx
        subl    $36, %esp      // alloc 36 bytes for local vars
        movl    $11, 8(%esp)     // store 11 in z
        movl    $6, 4(%esp)      // store 6 in y 
        movl    $2, (%esp)    // store 2 in x
        call    calc         // function call to calc
        movl    %eax, 20(%esp)  // %esp + 20 into %eax
        movl    $11, 16(%esp)  // WHAT
        movl    $6, 12(%esp)  // WHAT
        movl    $2, 8(%esp)  // WHAT
        movl    $.LC0, 4(%esp)  // WHAT?!?!
        movl    $1, (%esp) // move result into address of %esp
        call    __printf_chk  // call printf function
        addl    $36, %esp  // WHAT?
        popl    %ecx 
        popl    %ebp
        leal    -4(%ecx), %esp
        ret
        .size   main, .-main
        .ident  "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
        .section        .note.GNU-stack,"",@progbits

元のコード:

#include <stdio.h>

int calc(int x, int y, int z);

int main()
{
        int x = 2;
        int y = 6;
        int z = 11;
        int result;

        result = calc(x,y,z);

        printf("x=%d, y=%d, z=%d, result=%d\n",x,y,z,result);
}
4

1 に答える 1

11

有用なコンパイル コマンドは表示されませんでしたが、最適化が有効になっているように見えるため、実際にはローカル変数用のスペースがなく、最適化されています。

main:
        leal    4(%esp), %ecx   
        andl    $-16, %esp      
        pushl   -4(%ecx)     
        pushl   %ebp     
        movl    %esp, %ebp

上記のすべてのコードは、スタック フレームを設定します。これは であるためmain、標準のスタック フレームとは少し異なります。念のため、 とのスタックの位置合わせを保証しますandl $-16, %esp

        pushl   %ecx

位置合わせ補正前の元の値を保存espし、最後に復元します。

        subl    $36, %esp

ローカル変数ではなく、パラメーターの呼び出しに 36 バイトのスタック領域を割り当てます。

        movl    $11, 8(%esp)
        movl    $6, 4(%esp)
        movl    $2, (%esp)

calc右から左に呼び出すための引数、つまり定数を設定し(2, 6, 11)ます。

        call    calc         // function call to calc

calcが指す引数で function を呼び出しますesp

        movl    %eax, 20(%esp)
        movl    $11, 16(%esp)
        movl    $6, 12(%esp)
        movl    $2, 8(%esp)
        movl    $.LC0, 4(%esp)
        movl    $1, (%esp)

これらは、 を呼び出すための引数で__printf_chk、右から左に: です(1, .LC0, 2, 6, 11, %eax)。ここで、 は(ローカル変数がないことを思い出してください!)%eaxの戻り値であり、 はリテラル文字列のアドレスです。アセンブリの上部にある次の行を見てください。calc().LC0

.LC0:
        .string "x=%d, y=%d, z=%d, result=%d\n"

しかし、その神秘的なものはどう1ですか?ええと、Ubuntu では、標準のコンパイル オプション ( -D_FORTIFY_SOURCE) は、引数に追加のチェックを行う、またはそのようなものprintfに転送するインライン関数を作成します。__printf_chk(1, ...)

        call    __printf_chk

printfこれが代用関数の呼び出しです。

        addl    $36, %esp

これにより、スタックに追加された 36 バイトが削除されますsubl $36, %esp

        popl    %ecx 

これにより、アラインされていない可能性のあるスタック ポインターが に復元されますecx

        popl    %ebp
        leal    -4(%ecx), %esp

これにより、以前のスタック フレームが復元されます。

        ret

そして、 return を書いていないので、これは値なしで戻りますmain

于 2013-11-04T03:49:01.423 に答える