2

私はしばらくの間アセンブリを勉強しており、コツをつかみ始めていますが、理解できないように見えることの 1 つは、ローカルのローミングを終了するためにスタック ポインターをデクリメントする必要がある理由です。変数については、次のコードを見てください: (64 ビット GNU コンパイラ、AT&T 構文でコンパイルされたコード)

pushq   %rbp

movq    %rsp, %rbp

subq    $48, %rsp

call    __main
movl    $0, -4(%rbp)
movl    $4, -8(%rbp)
movl    -8(%rbp), %edx
movl    -4(%rbp), %eax
addl    %edx, %eax
movl    %eax, -12(%rbp)
movl    -4(%rbp), %edx
movl    -12(%rbp), %eax
addl    %eax, %edx
movl    -8(%rbp), %eax
addl    %edx, %eax
movl    %eax, -16(%rbp)
addq    $48, %rsp
popq    %rbp
ret

この小さなプログラムでは、esp を 48 ずつデクリメントする必要なく、これらすべてを実行できると想像できます。ベース ポインターを使用してスタックから値を移動したり、スタックに値を移動したり、esp を同じ位置にポイントさせて ebp をポップする準備ができているだけです。そして戻る。誰かがローカル変数のために「余地」を残す必要がある理由を明確にすることができますか? ありがとう!!これがばかげた質問のように思われる場合は、お詫び申し上げます

4

4 に答える 4

2

割り込みが発生した場合、RSP よりも小さいアドレスにあるものは何でも問題ありません。OS はユーザーに確認せずに消去 (つまり、独自のデータに置き換え) ます。割り込みは常に発生します。したがって、必要なものはすべて RSP 以下で保管する必要があります。

また、他の関数を呼び出すと、リターン アドレスがプッシュされます。RSP の上に空白がない限り、データが上書きされます。

于 2014-10-02T20:55:34.190 に答える
2

関数が呼び出すすべての関数に、変数をスタックのどこに配置したかを詳細に把握する必要がありますか?

多くの関数は他の関数を呼び出します - スタック ポインタをデクリメントすることは、関数が「スタックのこのビットを使用しています」と言う方法です。


「リーフ」メソッド - 他の関数を決して呼び出さないメソッド - は、提案したスタイルで実際に書くことができます - 他のコードがスタックを独自に使用することはないからです。

于 2014-10-01T07:10:59.170 に答える
0

スタック ポインターから定数を減算すると、ローカル変数にスペースが割り当てられます。コンパイラまたはアセンブラ プログラマは、rbp からの負のオフセット、または rsp からの正 (またはゼロ) のオフセットとして、これらの変数がどこにあるかを認識します。

あなたが示す例は少し奇妙です.いくつかの追加を実行し、結果をスタック上のローカル変数データに格納してから、スタックポインタに定数を追加し、それらのローカル変数をすべて効果的に解放します(合計を残します) eaxで)。また、例を見ると、使用される「最下位」アドレスは rbp-16 の 4 バイトのデータであるため、esp から 20 を引くだけで十分です (この場合)。

_alloca() などを使用すると、スタックから可変量のメモリが割り当てられるため、これはより複雑になる可能性があります。

また、rbp の使用はオプションです。サンプルコードでは「フレームポインター」として使用されていますが、一部のコンパイラーにはフレームポインターを無効にするオプションがあり、その場合、コンパイラーは rsp (または 32 ビットモードの場合は esp) のみを使用してローカル変数を追跡し、解放します。汎用レジスタとして使用される rbp (または ebp) をアップします。

于 2014-10-02T20:06:07.747 に答える
-3

関数の記憶に使用され、再帰的な関数呼び出しに非常に役立ちます。

于 2014-10-01T07:14:59.610 に答える