デバッガーはどのように機能するのでしょうか? 特に、すでに実行中の実行可能ファイルに「接続」できるもの。コンパイラがコードを機械語に変換することは理解していますが、デバッガはそれが何に接続されているかをどのように「認識」しますか?
7 に答える
デバッガーの動作の詳細は、デバッグ対象と OS によって異なります。Windows でのネイティブ デバッグについては、MSDN: Win32 Debugging APIで詳細を確認できます。
ユーザーは、アタッチするプロセスを名前またはプロセス ID でデバッガーに指示します。名前の場合、デバッガーはプロセス ID を検索し、システム コールを介してデバッグ セッションを開始します。Windows では、これはDebugActiveProcessになります。
アタッチすると、デバッガーは他の UI と同じようにイベント ループに入りますが、ウィンドウ システムからのイベントの代わりに、OS はデバッグ中のプロセスで発生したこと (たとえば、例外の発生) に基づいてイベントを生成します。WaitForDebugEventを参照してください。
デバッガーは、ターゲット プロセスの仮想メモリの読み取りと書き込みを行うことができ、OS によって提供される API を介してレジスタ値を調整することもできます。Windowsのデバッグ関数の一覧を参照してください。
デバッガーは、シンボル ファイルからの情報を使用して、アドレスからソース コード内の変数名と場所に変換できます。シンボル ファイル情報は別の API セットであり、OS のコア部分ではありません。Windows では、これはDebug Interface Access SDKを介して行われます。
管理された環境 (.NET、Java など) をデバッグしている場合、プロセスは通常似ていますが、仮想マシン環境は基盤となる OS ではなくデバッグ API を提供するため、詳細は異なります。
Linux では、プロセスのデバッグはptrace(2)システム コール で始まります。この記事ptrace
には、いくつかの単純なデバッグ構造を実装するために使用する方法に関する優れたチュートリアルがあります。
Windows OS を使用している場合、John Robbins による「Microsoft .NET および Microsoft Windows のアプリケーションのデバッグ」が優れたリソースになります。
(または古い版: "Debugging Applications" )
この本には、デバッガーの仕組みに関する章があり、いくつかの単純な (ただし動作する) デバッガーのコードが含まれています。
私は Unix/Linux のデバッグの詳細に詳しくないので、この内容は他の OS にはまったく当てはまらないかもしれません。しかし、非常に複雑な主題への導入として、概念 (詳細や API ではないにしても) は、ほとんどの OS に「移植」する必要があると思います。
ここで答える必要がある主な質問は 2 つあると思います。
1. デバッガーは例外が発生したことをどのように認識しますか?
デバッグ中のプロセスで例外が発生すると、ターゲット プロセスで定義されているユーザー例外ハンドラーに例外に応答する機会が与えられる前に、デバッガーは OS から通知を受けます。デバッガーがこの (最初のチャンスの) 例外通知を処理しないことを選択した場合、例外ディスパッチ シーケンスがさらに進み、ターゲット スレッドが必要に応じて例外を処理する機会が与えられます。SEH 例外がターゲット プロセスによって処理されない場合、デバッガーには、セカンド チャンス通知と呼ばれる別のデバッグ イベントが送信され、ターゲット プロセスで未処理の例外が発生したことが通知されます。ソース
2. デバッガーはブレークポイントで停止する方法をどのように認識していますか?
簡単な答えは次のとおりです。プログラムにブレークポイントを配置すると、デバッガーはその時点でコードをソフトウェア割り込みである int3 命令に置き換えます。結果として、プログラムは中断され、デバッガーが呼び出されます。
デバッグを理解するためのもう1つの貴重な情報源は、インテルCPUマニュアル(インテル®64およびIA-32アーキテクチャーソフトウェア開発者マニュアル)です。ボリューム3Aの第16章では、特別な例外やハードウェアデバッグレジスタなど、デバッグのハードウェアサポートが導入されました。以下はその章からのものです:
T(トラップ)フラグ、TSS — TSSにTフラグが設定されているタスクに切り替えようとすると、デバッグ例外(#DB)が生成されます。
WindowまたはLinuxがこのフラグを使用するかどうかはわかりませんが、その章を読むことは非常に興味深いことです。
これが誰かを助けることを願っています。
私の理解では、アプリケーションまたはDLLファイルをコンパイルするとき、コンパイルするものにはすべて、関数と変数を表すシンボルが含まれています。
デバッグビルドがある場合、これらのシンボルはリリースビルドの場合よりもはるかに詳細であるため、デバッガーはより多くの情報を提供できます。デバッガーをプロセスに接続すると、現在アクセスされている関数が確認され、ここから使用可能なすべてのデバッグシンボルが解決されます(コンパイルされたファイルの内部がどのように見えるかがわかっているため、メモリ内に何があるかを確認できます。 、int、float、stringsなどのコンテンツを含む)。最初のポスターが言ったように、この情報とこれらの記号がどのように機能するかは、環境と言語に大きく依存します。