-1

今日、私の心は関数ポインターのトピックについてさまよっていました。そして、頭の中で次のシナリオを思いつきました。

__stdcall int function (int)
{
    return 0;
}

int main()
{
    (*(int(*)(char*,char*))function)("thought", "experiment");
    return 0;
}

このコードはスタックを破壊するので、このコードを実行した場合、どのような種類の問題が発生する可能性がありますか?

私は自分自身を調査してこれを行いますが、開発マシンから1週間離れています。

編集:ちょっと待ってください、私はもう少し考えていました。コメントで観察されているように、このコードの目的は、すべてが言われ終わったときにスタックにパラメーターを残すことでした(呼び出し元はスタックに2つのパラメーターを置き、呼び出し先は1つのパラメーターのみを期待します)は1つだけをポップします)。しかし、私のキャストは呼び出し規約について言及していないので、少なくとも発信者の観点からは、stdcallをキャストしていませんか?int function(int)は引き続きパラメーターをスタックからポップしますが、呼び出し元はキャストのために関数が__cdecl(デフォルト)であると考えるように戻りますか?(つまり、合計3つのパラメーターがポップされましたか?)

EDIT2:ロブによって確認されたように、その2番目の質問に対する答えはイエスです。スタックにパラメータを残したい場合は、__stdcallを言い換える必要があります。

(*(__stdcall int(*)(char*,char*))function)("thought", "experiment");
4

3 に答える 3

2

_cdeclであるかのように関数を呼び出しています。これは、呼び出し元が引数をプッシュしてスタックをクリーンアップすることを意味します。

受信関数は_stdcallであり、これは呼び出し先がスタックをクリーンアップすることを意味します。呼び出し先は単一の引数を期待しているため、スタックから4バイトをポップします。

関数が戻ると、呼び出し元は2つのポインター(以前に2つのポインターをプッシュしたことがある)をポップオフするため、スタックは4バイト破損しています。

両方の呼び出し規約は同じ戻りメカニズムを使用し、同じレジスタルールを持っています(eax、ecx、およびedxは保持されません)。詳細については、ウィキペディアを参照してください。

スタックフレームのレイアウトと配置によっては、この不一致により多くの影響が生じる可能性があります。あなたが運が良ければ、あなたはそれで逃げます。そうでない場合は、メイン関数のリターンアドレスを台無しにして、who-knows-whereに分岐したときにプログラムがクラッシュする可能性があります。コンパイラが破損をキャッチするために何らかのスタックガードを挿入した場合、コンパイラはこれを検出してプログラムを中止する可能性があります。

于 2009-03-30T01:42:04.487 に答える
1

いいえ、ブルー スクリーンは発生しません。これを行うことができるユーザー モード プロセスはありません。このようなバグがカーネル モード コードにあったとしても、BSOD は、無効なメモリにアクセスするか、関数に間違った引数を渡した後にのみ発生します。

プロセスのプライベート メモリを破損しているだけであり、後で無効な操作が行われる可能性があります (無効なメモリを指すポインタの逆参照など)。これが発生すると、OS はプロセスを終了しますが、すぐには終了しません。

于 2009-03-30T01:20:08.113 に答える
0

この場合、「未定義の動作」が発生すると思います。

C標準から:(C ++でも同じだと思います)

768 変換されたポインターを使用して、ポイント先の型と互換性のない型を持つ関数を呼び出す場合、動作は未定義です。

編集: ほとんどのオペレーティング システムでは、このタイプのエラーがオペレーティング システム全体に問題を引き起こすことはありません。ただし、プログラムで未定義の問題が発生する可能性があります。ユーザー モード プログラムがブルー スクリーンを引き起こすことは非常に困難です。

于 2009-03-30T01:08:57.717 に答える