1

私はアセンブラーに少し慣れていませんが、esp埋め込まれたアセンブラーコードを使用して、スタック内のC++メソッドからパラメーターを検索しようとしています。espこれまでのところ、ポインタをにコピーすることすらできなかったebpので、スタックを保持することができます(スタックが変更された場合に備えて)。この小さなコードでさえ私に失敗を与えます:

#include <stdlib.h>

int main(int argc, char* argv[])
{
    __asm
    {
        mov ebp, esp
    }
    system("pause");
    return 0;
}

これを実行すると、次のようになります。

実行時チェックの失敗#0-ESPの値は、関数呼び出し全体で適切に保存されませんでした。これは通常、ある呼び出し規約で宣言された関数を、別の呼び出し規約で宣言された関数ポインターで呼び出した結果です。

何をすべきかわからない。助けてください、そして事前に感謝します。

4

1 に答える 1

2

EBP一般に、 の値を に移動する前に、 の現在の値をスタックにESPプッシュする必要がありますEBPEBPは、32 ビット プラットフォームの「callee-save」レジスタです。つまり、関数内で変更する場合は、最初に保存する必要があります。

関数がスタックが指している場所 (または関数呼び出しの後に戻る場所) の値を返すようにしたい場合、最善の方法は次のとおりです。

void* get_stack_addr()
{
        void* stack_ptr = NULL;

        //on 32-bit systems
        //EBP is pointing at top of stack frame
        //EBP + 4 is the return instruction address
        //EBP + 8 is the value that was in ESP before function call for a function with no arguments
        __asm__
        (
                "movl %%ebp, %0\n\t"
                "addl $8, %0\n\t"
                : "=r" (stack_ptr)
        );

        return stack_ptr;
}

その方法EAXは、呼び出しが行われる前にスタックが指していた値のアドレスを保持していますget_stack_addr()。関数での値を返しただけでは、ESP実際にはどこを指しているのかわかりません。これは、適切なスタック アライメントを維持するために、コンパイラが C/C++ 関数のスタックをパディングすることがよくあるためです。また、多くの場合、すべてのローカル変数用にスタックにスペースを確保しますが、これもスタックの計算を中断します。スタック フレームの先頭を指す を使用EBPすることで、関数呼び出し前のスタックの値を 32 ビット プラットフォームで正確に計算できます。最後にEAX、C/C++ のほとんどの OS アプリケーション バイナリ インターフェイスでEAXは、 ではなく関数の戻り値を保持するため、戻り値を に配置しますEBP

もう1つ... を呼び出す実際の関数のスタック上のパラメーターの開始が必要な場合はget_stack_addr()、に変更movl %%ebp, %0\n\tmovl (%%ebp), %0)\n\tます。そうすれば、以前のスタック フレーム ベース ポインター (つまり、呼び出し元のスタック フレーム ベース ポインター) を取得でき、そのアドレスに +8 の値を追加することで、格納されているパラメーターの先頭のいずれかを取得できます。または、現在の関数の呼び出し元のスタック フレーム (つまり、前のスタック フレーム) を指しているアドレスを見ています。


拡張機能として、次のもの"leal 8(%%ebp), %0\n\t"を置き換えることができます。

"movl %%ebp, %0\n\t"
"addl $8, %0\n\t"

このleal命令は、EBPの値に 8 を加算し、結果を出力オペランドに格納します。

于 2011-05-05T19:03:58.267 に答える