web-dev-qa-db-ja.com

x86用のGCCによって生成された「Push%ebp; movl%esp、%ebp」の用途は何ですか?

これらの2つの命令がx86マシン用にgccによって生成されるアセンブリコードにもたらす影響:

Push %ebp
movl %esp, %ebp
35
mahesh

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:の前のすべてのもの)を説明するために、最初に次のようにします。

  1. pushl %ebpcalling関数のスタックフレームをスタックに格納します。
  2. movl %esp, %ebpは、現在のスタックポインターを受け取り、それをcalled関数のフレームとして使用します。
  3. subl $16, %espは、ローカル変数のための余地を残します。

これで、関数のビジネス準備が整いました。 %ebp%レジスタからの負のオフセットを持つ参照は、ローカル変数(この例ではx)です。 %ebp%レジスタからの正のオフセットを持つ参照は、渡されるパラメーターです。

最後の注目点は、呼び出し元の関数のスタックフレームを復元する作業を行うx86アセンブラー命令であるleave命令です。これは通常、Cコードのより高速なmove %ebp %espおよびpop %ebp%シーケンスに最適化されます。ただし、説明のために、最適化をまったく行わずにコンパイルしました。

これは、関数の最初にある典型的なコードです。

EBPレジスタの内容をスタックに保存し、現在のスタックポインターの内容をEBPに格納します。

スタックは、関数呼び出し中にローカル引数を格納するために使用されます。ただし、関数では、値がスタックに格納されるため、スタックポインターが変更される場合があります。

スタックの元の値を保存すると、EBPレジスタを介して格納された引数を参照できますが、スタックは引き続き使用(値を追加)できます。

関数の最後に、おそらくコマンドが表示されます

pop %ebp   ; restore original value 
ret        ; return 
9
Roalt
Push %ebp

これにより、32ビット(拡張)ベースポインターレジスタがスタックにプッシュされます。つまり、スタックポインター(%esp)が4減算され、%ebpの値がスタックポインターが指す場所にコピーされます。

movl %esp, %ebp

これにより、スタックポインタレジスタがベースポインタレジスタにコピーされます。

スタックポインターをベースポインターにコピーする目的は、スタックフレーム、つまり、サブルーチンがローカルデータを格納できるスタック上の領域を作成することです。サブルーチン内のコードは、ベースポインターを使用してデータを参照します。

7
Guffa

function prolog と呼ばれるものの一部です。

関数の終了時に取得される現在のベースポインターを保存し、新しいebpを新しいフレームの先頭に設定します。

2
mgv

また、後で注意することも重要だと思いますPush %ebpおよびmovl %esp, %ebpアセンブリにはPush %ebxまたはPush %edx。これらは、レジスタの呼び出し元の保存%ebxおよび%edx。ルーチン呼び出しの最後に、レジスタは元の値で復元されます。

また-%ebx, %esi, %ediはすべて呼び出し先保存レジスタです。したがって、それらを上書きする場合は、まず保存してから復元する必要があります。

1
Alex Spencer

このコードにより、プログラムのスタックが設定されます。

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

このコードは、関数のスタックを設定します。

詳細については、これを参照してください 質問

この助けを願っています!

0