14

私は、サボテン スタックを使用して並列プログラムを実装する MS Windows の言語であるPARLANSEを実装しました。スタック チャンクは関数ごとに割り当てられ、ローカル変数、式の一時的なプッシュ/ポップ、およびライブラリの呼び出し (ライブラリ ルーチンが動作するためのスタック スペースを含む) を処理するのにちょうどいいサイズです。このようなスタック フレームは、実際には 32 バイトまで小さくすることができます。

コードが愚かなことをしてハードウェアトラップを引き起こさない限り、これはすべてうまく機能します...その時点で、Windowsはx86マシンコンテキスト全体を「スタックに」プッシュすることを主張しているようです。FP/MMX などを含めると、これは約 500 バイト以上になります。登録します。当然のことながら、32 バイトのスタックで 500 バイトのプッシュを行うと、クラッシュしてはならないものが破壊されます。(ハードウェアはトラップにいくつかの単語をプッシュしますが、コンテキスト全体ではありません)。

[編集 2012 年 11 月 27 日: Windows が実際にプッシュするスタックのばかげた量の測定された詳細については、これを参照してください]

Windows に例外コンテキスト ブロックを別の場所 (スレッド固有の場所など) に保存させることはできますか? その後、ソフトウェアはスレッドで例外ヒットを取得し、小さなスタック フレームをオーバーフローさせることなく処理できます。

これは不可能だと思いますが、もっと多くの聴衆に聞いてみようと思いました。これを引き起こす可能性のあるOS標準の呼び出し/インターフェースはありますか?

OS で実行するのは簡単です。MS に、デフォルトで現在の従来の動作を有効にするために初期化されるコンテキスト ストレージの場所「contextp」をオプションで定義させることができれば、簡単なことです。次に、割り込み/トラップ ベクター コードを置き換えます。

  hardwareint:   push  context
                mov   contextp, esp

... と ...

  hardwareint:  mov <somereg> contextp
                test <somereg>
                jnz  $2
                push  context
                mov   contextp, esp
                jmp $1 
         $2:    store context @ somereg
         $1:    equ   *

someregなどを保存するために必要な明らかな変更があります。

[私が今やっていることは、各関数の生成されたコードをチェックすることです。トラップを生成する可能性がある場合 (例: ゼロ除算)、またはデバッグ中 (不正なポインター deref の可能性など) がある場合は、FP コンテキスト用にスタック フレームに十分なスペースを追加します。スタック フレームのサイズは ~~ 500 ~ 1000 バイトになり、プログラムはそこまで再帰できなくなります。したがって、実行可能な解決策がありますが、デバッグが複雑になります]

編集 8 月 25 日: 私はこの話を、MS の誰が実際に気にかけているかを明らかにする権限を持っている Microsoft の社内エンジニアに伝えることができました。解決へのかすかな希望があるかもしれません。

編集 9 月 14 日: MS Kernal Group Architect はこの話を聞いて同情しています。彼は、MS は (提案されたような) 解決策を検討するだろうが、サービス パックに含まれる可能性は低いと述べた。Windows の次のバージョンを待つ必要があるかもしれません。(はぁ…老けるかも…)

編集: 2010 年 9 月 13 日 (1 年後)。Microsoft 側でのアクションはありません。私の最近の悪夢: Windows X64 で 32 ビット プロセスを実行しているトラップを取得すると、割り込みハンドラーが 32 ビット コンテキストをプッシュするように偽装する前に、X64 コンテキスト全体がスタックにプッシュされますか? それはさらに大きくなります(整数レジスタの2倍、幅の2倍、SSEレジスタの2倍(?))?

編集: 2012 年 2 月 25 日: (1.5 年が経過しました...) Microsoft 側からの反応はありません。彼らは私のような並列性を気にしていないだけだと思います。これはコミュニティへの不利益だと思います。通常の状況下で MS が使用する「ビッグ スタック モデル」は、大量の VM を消費することにより、任意の瞬間に実行できる並列計算の量を制限します。PARLANSE モデルを使用すると、実行中/待機中のさまざまな状態で 100 万個のライブ「グレイン」を持つアプリケーションを作成できます。これは、1 億ノードのグラフが「並行して」処理される一部のアプリケーションで実際に発生します。PARLANSE スキームは、約 1Gb の RAM でこれを行うことができ、かなり扱いやすいです。MS 1Mb の「ビッグ スタック」でそれを試した場合、スタック スペースのためだけに 10^12 バイトの VM が必要になります。

編集: 2014 年 4 月 29 日: (4 年が経ちました)。 MSはSOを読んでいないと思います。 私は PARLANSE で十分なエンジニアリングを行ったので、デバッグ中または FP 操作が進行中の場合にのみ大きなスタック フレームの代償を支払うだけなので、これに対処するための非常に実用的な方法を見つけることができました。MS は失望し続けています。さまざまなバージョンの Windows によってスタックにプッシュされるものの量は、ハードウェア コンテキストの必要性を超えて、大幅かつ著しく異なるようです。この変動性の一部は、MS 以外の製品 (ウイルス対策など) が例外処理チェーンに鼻を突っ込んでいることが原因であるというヒントがあります。なぜ彼らは私のアドレス空間の外からそれを行うことができないのですか? いずれにしても、FP/デバッグ トラップに大きなスロップ ファクターを追加するだけで、これらすべてを処理し、フィールドで必然的な MS システムがその量を超えるのを待ちます。

4

5 に答える 5

4

基本的に、多くの割り込みハンドラーを再実装する必要があります。つまり、割り込み記述子テーブル(IDT)に接続する必要があります。問題は、kernelmode-> usermodeコールバックも再実装する必要があることです(SEHの場合、このコールバックはに存在しntdll.dll、名前が付けられKiuserExceptionDispatcherます。これにより、すべてのSEHロジックがトリガーされます)。重要なのは、システムの残りの部分はSEHが現在のように機能することに依存しており、システム全体で実行しているため、ソリューションが問題を引き起こす可能性があるということです。たぶん、あなたは割り込みの時にあなたがどのプロセスにいるのかをチェックすることができます。ただし、全体的な概念はエラーが発生しやすく、システムの安定性に非常に悪影響を及ぼします。
これらは実際にはルートキットのような手法です。

編集:
いくつかの詳細:割り込みハンドラーを再実装する必要がある理由は、例外(たとえば、ゼロ除算)は本質的にソフトウェア割り込みであり、それらは常にIDTを通過するためです。例外がスローされると、カーネルはコンテキストを収集し、例外をユーザーモードに戻します(前述のntdllのKiUserExceptionDispatcherを介して)。この時点で干渉する必要があるため、ユーザーモードに戻るためのメカニズムも提供する必要があります。(ntdllには、カーネルモードからのエントリポイントとして使用される関数があります。名前は覚えていませんが、KiUserACPで何か.....)

于 2009-06-17T12:19:14.980 に答える
3

パラメーター/ローカル スタックを実際のものから分離することを検討してください。別のレジスタ (EBP など) を有効なスタック ポインターとして使用し、ESP ベースのスタックを Windows の希望どおりにします。

PUSH/POP は使用できなくなりました。PUSH の代わりに SUB/MOV/MOV/MOV コンボを使用する必要があります。しかし、OS にパッチを当てるよりはましです。

于 2013-12-04T14:57:51.557 に答える
1

Windows が x86 ハードウェアを使用してトラップ コードを実装している場合、トラップに使用するゲートを変更するには、リング 0 アクセス (ドライバーまたは API 経由) が必要です。

x86 のゲートの概念は、次のいずれかを指します。

  • リターン アドレスを含むレジスタ コンテキスト全体が現在のスタック (= 現在の esp) にプッシュされている間に呼び出される割り込みアドレス (コード セグメント + オフセット ポインタ)、または
  • 別のタスクに切り替えるタスク記述子 (ハードウェアがサポートするスレッドと見なすことができます)。代わりに、関連するすべてのデータがそのタスクのスタック (esp) にプッシュされます。

もちろん、後者が必要です。Wineがそれをどのように実装したかを調べれば、Google に尋ねるよりも効果的かもしれません。

私の推測では、残念ながら x86 で動作させるにはドライバーを実装する必要があり、Wikipediaによると、ドライバーが IA64 プラットフォームでドライバーを変更することは不可能です。2 番目に最適なオプションは、トラップからのコンテキスト プッシュが常に収まるように、スタック内のスペースをインターリーブすることでしょうか?

于 2009-06-15T12:44:48.867 に答える
1

コメント欄が埋まりました…

とにかく、ベクトルがどこを指しているのかわかりません。SDDの回答と「KiUserExceptionDispatcher」への言及に基づいてコメントを作成していました...さらに検索する場合を除いて(http://www.nynaeve.net/?p=201)それこの時点では手遅れのようです。

SIDTはリング 3 で実行できます...これにより、割り込みテーブルの内容が明らかになり、セグメントをロードして、少なくともテーブルの内容を読み取ることができる場合があります。運が良ければ、(たとえば) vector 0/divide by zero のエントリを読み取り、ハンドラの内容を読み取ることができます。

この時点で、コードをシステム ファイルと一致させるために 16 進バイトを一致させようとしますが、コードが属しているファイルを特定するためのより良い方法があるかもしれません (必ずしも DLL ではなく、win32k.sys または動的に生成される可能性があります。ユーザーモードから物理メモリレイアウトをダンプする方法があるかどうかはわかりません。

他のすべてが失敗した場合は、カーネル モード デバッガーをセットアップするか、割り込みテーブルとメモリ レイアウトを直接表示できるWindows ( Bochs ) をエミュレートすることができます。次に、CONTEXT が押されるポイントまでトレースし、それが発生する前に制御を取得する機会を探すことができます。

于 2009-08-31T22:49:33.143 に答える
0

Windows の例外処理は SEH と呼ばれます。IIRC を無効にすることはできますが、使用している言語のランタイムが気に入らない場合があります。

于 2009-06-15T08:34:00.157 に答える