UEFIアプリケーションの例外

UEFIに精通しているプログラマーなら誰でも、組み込みの例外処理メカニズムがないことを知っています。 Microsoft C / C ++コンパイラの拡張であるtry / exceptブロックについて話している。 そのようなメカニズムを持ち、それが提供する利点を最大限に活用することは非常に便利です。 したがって、この記事では、この問題の解決に焦点を当てます。 また、この記事に添付されているのは、UEFIアプリケーションに基づいたデモを含むメカニズムの完全な実装です。 Intel 64ビットプロセッサのみが影響を受け、議論で暗示されています。 このメカニズムの実装は、gitリポジトリーの例外フォルダーhttps://github.com/anatolymik/machineries.gitにあります



最初に、例外が一般的にどのように処理されるかについて少し話しましょう。 まず、例外は特別な状況です。 原則として、プロセッサが特定の命令を実行できないため、例外が発生します。 このようなイベントが発生すると、プロセッサは、エントリポイントがIDTテーブルにある例外ハンドラを呼び出します。 プロセッサーには多くの例外があるため、対応するハンドラーはそのタイプに応じて呼び出されます。









上の図からわかるように、例外ハンドラは256個しかなく、さらに最初の32個は例外用に予約されています。 残りは割り込みの処理に使用されますが、ここでは説明しません。 既に述べたように、対応するハンドラーは例外のタイプに従って呼び出されます。 たとえば、数値を0で除算すると、例外ハンドラー0が呼び出されます。または、プロセッサがストリームでなじみのない命令に遭遇すると、例外ハンドラー6が呼び出されます。詳細については、Intel 64およびIA-32アーキテクチャソフトウェア開発者マニュアルを参照してください。 プロセッサのハードウェアに触れただけで、ハードウェアエラーの処理は不可能ですが、それだけではありません。



try / exceptブロックについては、コンパイラがブロックを検出すると、実行可能ファイル内の各ブロックの場所を記述するメタ情報を生成することに注意する必要があります。 この情報は、別の場所、より正確には、PEイメージの.pdataセクションにあります。









図からわかるように、実行可能ファイルに関するコード、データ、およびさまざまな情報(たとえば、この例の場合、try / exceptブロックの記述子)は単一のイメージに配置されています。 このデータはすべて、データの種類に応じて、いわゆるセクションに分散されます。 .pdataセクションの説明に戻ると、各try / exceptブロックについて、その場所に加えて、ハンドラーのアドレスも保存されることに注意してください。 通常、これはexceptおよびfinallyブロックで囲まれたコードです。 PEファイル形式の詳細については、Microsoft Portable Executable and Common Object File Format Specificationを参照してください。 検討中のメカニズムの機能に必要なソフトウェアに触れたばかりですが、今度はこれを包括的にもう一度見ていきます。



try / exceptブロックはコード生成の過程でコンパイラーによって処理され、一般にそれらは言語自体のメカニズムであるという事実にもかかわらず、それらの操作にはオペレーティングシステムからのサポートが必要です。 さらに、処理の一部はオペレーティングシステムによって実行され、一部は実行可能ファイル自体によって実行されることに注意してください。 実行可能ファイル自体によって実行されますが、コンパイラによって生成されない部分もあり、代わりに実装する必要があります。 たとえば、予約名__c_specific_handlerは、例外の処理を担当する関数の名前です。 実装がない場合、リンカは実行可能ファイルを生成できません。 Windows用の開発ツールでは、これらの機能の実装は、アプリケーションにバンドルされているライブラリに含まれています。









上の図に示すように、例外が発生すると、プロセッサーはIDTテーブルから対応するハンドラーを呼び出します。 例外ハンドラは、初期化中にオペレーティングシステムによって設定されます。 例外の時点で、オペレーティングシステム自体が制御を受け取ります。 すべての例外のハンドラーは非常によく似ており、処理に必要なすべての情報と特定の種類の例外に固有の情報を格納します。 次に、これらのハンドラーはすべて、ハンドラーを検索して呼び出す機能を呼び出します。 この関数は、例外が発生した特定のコードセクションに一致するハンドラーを見つけるために、.pdataセクションをスキャンします。 ハンドラーが見つかった場合、オペレーティングシステムはハンドラーに制御を渡します。それ以外の場合、アプリケーションはエラーで終了します。 例外を発生させて処理するプロセスを調べたので、例外を完全に機能させるためにUEFIアプリケーションで何を行う必要があるかについて議論を開始できます。 また、説明は非常に簡略化されていることに注意してください。



UEFIには例外とは何の関係もないため、上記のすべてを実装する必要があることは明らかです。 さらに、リストのプロセスでは、記事に添付されている実装からのソースコードの調査を容易にするために、その機能、ファイル、およびフォルダーも参照します。 まず、例外ハンドラーを実装する必要があります。 これらは、excフォルダーのexccpu.asmファイルにあります。 IDTでアドレスを設定する必要もあります。これは、excフォルダーのexccpu.cppファイルにあるCpuInitializeExceptionHandlers関数によって実行されます。 これらのハンドラーのほとんどすべてが、exccpu.cppファイルからCpuDispatchException関数を呼び出します。これは、実際には例外ハンドラーの検索と呼び出しの開始点です。 excフォルダーのexcdsptch.cppファイルにあるDispatchExceptionおよびUnwindStack関数は、例外ハンドラーの検索と呼び出しを行います。 .pdataセクションをスキャンするには、PEイメージの処理機能を実装する必要があります。 これらの関数は、前述の例外ハンドラーの検索と呼び出しの関数によって使用されます。 PEイメージのスキャン機能の実装は、別のフォルダー-peにグループ化されます。 最後に、__ C_specific_handlerおよび_local_unwind予約関数の実装が必要です。 両方の機能は、excフォルダーのexcchandler.cppファイルに実装されています。 アプリケーションへのエントリポイントは、プロジェクトルートのefi.cppファイルにあるEfiMain関数です。 IDTでハンドラーのアドレスを設定することは、デモンストレーションのために簡略化されていることに注意してください。 完全な実装について話す場合は、孤立したIDTが必要です。



おそらくそれだけです。 このメカニズムの説明は、さまざまな詳細でインターネット上で繰り返し取り上げられていることを付け加える価値があります。 また、この記事は、実装とデモが添付されているためユニークです。 最初は、Windows環境外で動作するプロジェクトにこのメカニズムを実装する必要があったとき、前述の説明では十分ではありませんでした。 含めるにはWindowsを逆にする必要があり、UNWIND_INFOバージョン2の説明はまったく見つかりませんでした。 そのため、この記事はもともと別の形式で、非常に詳細な形式で考案されていましたが、読んだ後は質問は残りません。 実際には、それを書くことは、記事ではなく、文書を書くことに退化しました。 最初から、どの問題が解決されているのかは明確ではありません。 そのため、詳細なしで、このようなシンプルで簡単なオプションを作成しました。 詳細なバージョンは、部分的に公開される予定です。



All Articles