これらの2つの命令がx86マシン用にgccによって生成されるアセンブリコードにもたらす影響:
Push %ebp
movl %esp, %ebp
unwindの説明は文字通りの真実(1つの小さな方向エラーにもかかわらず)ですが、その理由は説明されていません。
%ebp
は、スタックフレームの「ベースポインター」です。これは、Cランタイムがスタック上のローカル変数とパラメーターにアクセスするために使用するポインターです。これは、GCCによって生成された典型的な関数プロローグコードです(正確にはg ++)。最初にC++ソースです。
// junk.c++
int addtwo(int a)
{
int x = 2;
return a + x;
}
これにより、次のアセンブラが生成されます。
.file "junk.c++"
.text
.globl _Z6addtwoi
.type _Z6addtwoi, @function
_Z6addtwoi:
.LFB2:
pushl %ebp
.LCFI0:
movl %esp, %ebp
.LCFI1:
subl $16, %esp
.LCFI2:
movl $2, -4(%ebp)
movl -4(%ebp), %edx
movl 8(%ebp), %eax
addl %edx, %eax
leave
ret
.LFE2:
.size _Z6addtwoi, .-_Z6addtwoi
.ident "GCC: (Ubuntu 4.3.3-5ubuntu4) 4.3.3"
.section .note.GNU-stack,"",@progbits
次に、そのプロローグコード(.LCFI2:
の前のすべてのもの)を説明するために、最初に次のようにします。
pushl %ebp
calling関数のスタックフレームをスタックに格納します。movl %esp, %ebp
は、現在のスタックポインターを受け取り、それをcalled関数のフレームとして使用します。subl $16, %esp
は、ローカル変数のための余地を残します。これで、関数のビジネス準備が整いました。 %ebp%
レジスタからの負のオフセットを持つ参照は、ローカル変数(この例ではx
)です。 %ebp%
レジスタからの正のオフセットを持つ参照は、渡されるパラメーターです。
最後の注目点は、呼び出し元の関数のスタックフレームを復元する作業を行うx86アセンブラー命令であるleave
命令です。これは通常、Cコードのより高速なmove %ebp %esp
およびpop %ebp%
シーケンスに最適化されます。ただし、説明のために、最適化をまったく行わずにコンパイルしました。
これは、関数の最初にある典型的なコードです。
EBPレジスタの内容をスタックに保存し、現在のスタックポインターの内容をEBPに格納します。
スタックは、関数呼び出し中にローカル引数を格納するために使用されます。ただし、関数では、値がスタックに格納されるため、スタックポインターが変更される場合があります。
スタックの元の値を保存すると、EBPレジスタを介して格納された引数を参照できますが、スタックは引き続き使用(値を追加)できます。
関数の最後に、おそらくコマンドが表示されます
pop %ebp ; restore original value
ret ; return
Push %ebp
これにより、32ビット(拡張)ベースポインターレジスタがスタックにプッシュされます。つまり、スタックポインター(%esp)が4減算され、%ebpの値がスタックポインターが指す場所にコピーされます。
movl %esp, %ebp
これにより、スタックポインタレジスタがベースポインタレジスタにコピーされます。
スタックポインターをベースポインターにコピーする目的は、スタックフレーム、つまり、サブルーチンがローカルデータを格納できるスタック上の領域を作成することです。サブルーチン内のコードは、ベースポインターを使用してデータを参照します。
function prolog と呼ばれるものの一部です。
関数の終了時に取得される現在のベースポインターを保存し、新しいebpを新しいフレームの先頭に設定します。
また、後で注意することも重要だと思いますPush %ebp
およびmovl %esp, %ebp
アセンブリにはPush %ebx
またはPush %edx
。これらは、レジスタの呼び出し元の保存%ebx
および%edx
。ルーチン呼び出しの最後に、レジスタは元の値で復元されます。
また-%ebx, %esi, %edi
はすべて呼び出し先保存レジスタです。したがって、それらを上書きする場合は、まず保存してから復元する必要があります。
このコードにより、プログラムのスタックが設定されます。
x86では、スタック情報は2つのレジスターによって保持されます。
Base pointer (bp): Holds starting address of the stack
Stack pointer (sp): Holds the address in which next value will be stored
これらのレジスタは、モードごとに異なる名前を持っています。
Base pointer Stack pointer
16ビットリアルモード:bp sp
32ビットプロテクトモード:ebp(%ebp)esp(%esp)
64ビットモード:rbp rsp
</ code>
スタックを設定すると、スタックポインターとベースポインターは最初に同じアドレスを取得します。
次に、コードを説明します。
Push %ebp
このコードは、現在のスタックアドレスをスタックにプッシュして、関数が適切に「終了」または「復帰」できるようにします。
movl %esp, %ebp
このコードは、関数のスタックを設定します。
詳細については、これを参照してください 質問 。
この助けを願っています!