web-dev-qa-db-ja.com

_chkstk()関数の目的は何ですか?

最近、 _/FAsu_ Visual C++コンパイラオプション を使用して、特に長いメンバー関数定義のソース+アセンブリを出力しました。アセンブリ出力では、スタックフレームが設定された後、不思議な_chkstk()関数が1回呼び出されます。

_chkstk() のMSDNページでは、この関数が呼び出される理由が説明されていません。スタックオーバーフローの質問も見ました スタックにページサイズを超えるバッファを割り当てるとメモリが破損しますか? ですが、OPと受け入れられた回答が何について話しているのかわかりません。

_chkstk() CRT関数の目的は何ですか?それは何をするためのものか?

31
Daniel Trebbien

スレッドが使用されるときに、スレッド用に追加のスタックにあるWindowsページ。スタックの最後に、アクセスできないメモリとしてマップされたガードページが1つあります。プログラムがそれにアクセスすると(現在マップされているよりも多くのスタックを使用しようとしているため)、アクセス違反が発生します。 OSは障害をキャッチし、古いガードページと同じアドレスにあるスタックの別のページにマップし、古いガードページのすぐ先に新しいガードページを作成し、違反の原因となった命令から再開します。

関数に複数ページのローカル変数がある場合、関数がアクセスする最初のアドレスは、スタックの現在の端を超えて複数ページになる可能性があります。したがって、ガードページを見逃し、OSが認識しないアクセス違反をトリガーします。これは、より多くのスタックが必要なためです。必要なスタックの合計が特に大きい場合は、ガードページを超えて、スタックに割り当てられた仮想アドレススペースの終わりを超えて、実際に他の目的で使用されているメモリに到達する可能性があります。

そう、 _chkstkは、ローカル変数のための十分なスペースがあることを保証します。これは、ローカル変数のメモリにページサイズの間隔で昇順でタッチして、ガードページ(いわゆる「スタックプローブ」)を見逃さないようにすることで実現できると想像できます。それが実際にそれを行うかどうかはわかりませんが、おそらくそれはより直接的なルートを取り、OSに特定の量のスタックにマップするように指示します。いずれにせよ、必要な合計がスタックに使用可能な仮想アドレススペースよりも大きい場合、OSは未定義の処理を行う代わりに、それについて文句を言うことができます。

39
Steve Jessop

__chkstkのコードを調べたところ、1ページ間隔で繰り返しスタックプローブが実行されます。したがって、この方法では、OSを呼び出す必要はありません。 raxのパラメーターは、追加するデータのサイズです。これにより、ターゲットアドレス(現在のrsp --rax)にアクセスできるようになります。 rax> rspの場合、アドレス0に対してこれを行います。興味深いショートカットとして、最初にアドレスをgs:[10h]と比較します。これは、マップされている現在の最下位ページです。ターゲットアドレス> = thisの場合、何もしません。

ちなみに、少なくとも64ビットコードの場合は、__chkstk__の2つのアンダースコアでつづられます。

7
Michael Rolle