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