3

バイナリ ファイルをよりよく理解するために、小さな C++ の例を用意し、gdb を使用して逆アセンブルし、マシン コードを探しました。

main()関数は関数を呼び出しますfunc():

int func(void)
{
    int a;
    int b;
    int c;
    int d;
    d = 4;
    c = 3;
    b = 2;
    a = 1;
    return 0;
}

プロジェクトは、デバッグ情報を保持したまま g++ でコンパイルされます。次に gdb を使用してソース コードを逆アセンブルします。私が得たものはfunc()次のようになります:

0x00000000004004cc <+0>:    push   %rbp
0x00000000004004cd <+1>:    mov    %rsp,%rbp
0x00000000004004d0 <+4>:    movl   $0x4,-0x10(%rbp)
0x00000000004004d7 <+11>:   movl   $0x3,-0xc(%rbp)
0x00000000004004de <+18>:   movl   $0x2,-0x8(%rbp)
0x00000000004004e5 <+25>:   movl   $0x1,-0x4(%rbp)
0x00000000004004ec <+32>:   mov    $0x0,%eax
0x00000000004004f1 <+37>:   pop    %rbp
0x00000000004004f2 <+38>:   retq

ここでの問題は、各整数値に 4 バイトが必要なため、スタック ポインターをベース ポインターよりも低いアドレスに 16 バイト移動する必要があることです。しかし、スタックポインタを移動せずに値がスタックに置かれているようです。

何が正しく理解できなかったのですか?これはコンパイラの問題ですか、それともアセンブラがいくつかの行を省略したのでしょうか?

宜しくお願いします

4

1 に答える 1

4

コンパイラにまったく問題はありません。コンパイラはコードのコンパイル方法を自由に選択でき、スタック ポインタを変更しないことを選択しました。関数は他の関数を呼び出さないため、そうする必要はありません。別の関数を呼び出した場合は、別のスタック フレームを作成して、呼び出し先が呼び出し元のスタック フレームを踏まないようにする必要があります。

原則として、コンパイラがコードをどのようにコンパイルするかについて、仮定を立てないようにする必要があります。たとえば、コンパイラは完全に自由に関数の本体を最適化できます。

于 2013-02-27T12:46:49.880 に答える