デバッガーはどのように機能するのでしょうか。特に、すでに実行中の実行可能ファイルに「接続」できるもの。コンパイラーがコードを機械語に変換することを理解していますが、デバッガーはそれが接続されているものをどのように「知る」のですか?
デバッガーはどのように機能するのでしょうか。特に、すでに実行中の実行可能ファイルに「接続」できるもの。コンパイラーがコードを機械語に変換することを理解していますが、デバッガーはそれが接続されているものをどのように「知る」のですか?
回答:
デバッガーの動作の詳細は、デバッグ対象と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を提供するため、詳細は異なります。
私が理解しているように:
x86のソフトウェアブレークポイントの場合、デバッガーは命令の最初のバイトをCC
(int3
)に置き換えます。これはWriteProcessMemory
Windowsで行われます。CPUがその命令に到達してを実行するint3
と、CPUはデバッグ例外を生成します。OSはこの割り込みを受け取り、プロセスがデバッグ中であることを認識し、ブレークポイントに達したことをデバッガプロセスに通知します。
ブレークポイントにヒットしてプロセスが停止した後、デバッガーはブレークポイントのリストを調べ、をCC
元々あったバイトに置き換えます。デバッガーはTF
、トラップフラグをEFLAGS
(を変更してCONTEXT
)設定し、プロセスを続行します。トラップフラグにより、CPU INT 1
は次の命令でシングルステップ例外()を自動的に生成します。
デバッグ中のプロセスが次回停止すると、デバッガーはブレークポイント命令の最初のバイトを再びに置き換えCC
、プロセスは続行されます。
これがすべてのデバッガーでどのように実装されているのか正確にはわかりませんが、このメカニズムを使用して自分自身をデバッグするWin32プログラムを作成しました。まったく役に立たないが、教育的。
Linuxでは、プロセスのデバッグはptrace(2)システムコールで始まります。 この記事には、ptrace
いくつかの簡単なデバッグ構造を実装するためのの使用方法に関する優れたチュートリアルがあります。
(2)
「ptraceはシステムコールです」以上のことを教えてくれますか?
(2)
マニュアルセクション番号です。マニュアルセクションの説明については、en.wikipedia.org / wiki / Man_page#Manual_sectionsをご覧ください。
ptrace
システムコールであることがわかります。
(2)
はman 2 ptrace
正しいマンページを入力して取得できることを示しています- ptrace
明確にするものは他にないため、ここでは重要ではありませんが、Linux と比較man printf
するためman 3 printf
です。
Windows OSを使用している場合、このための優れたリソースは、John Robbinsによる「Microsoft .NETおよびMicrosoft Windowsのアプリケーションのデバッグ」です。
(または以前のエディション:「アプリケーションのデバッグ」)
この本には、いくつかの単純な(しかし機能している)デバッガーのコードを含む、デバッガーの動作に関する章があります。
私はUnix / Linuxデバッグの詳細に精通していないので、これは他のOSにはまったく当てはまらないかもしれません。しかし、非常に複雑なテーマの紹介として、詳細やAPIでなくても、ほとんどのOSにコンセプトを「移植」する必要があると思います。
デバッグを理解するためのもう1つの貴重な情報源は、インテルCPUマニュアル(インテル®64およびIA-32アーキテクチャーソフトウェア開発者向けマニュアル)です。ボリューム3Aの第16章では、特別な例外やハードウェアデバッグレジスタなど、デバッグのハードウェアサポートを紹介しました。以下はその章からのものです:
T(トラップ)フラグ、TSS — TSSにTフラグが設定されているタスクに切り替えようとしたときに、デバッグ例外(#DB)を生成します。
WindowとLinuxのどちらがこのフラグを使用しているかはわかりませんが、その章を読むのはとても興味深いです。
これが誰かを助けることを願っています。
ここで答えるべき主な質問が2つあると思います。
1.デバッガーは例外が発生したことをどのようにして知るのですか?
デバッグ中のプロセスで例外が発生すると、ターゲットプロセスで定義されたユーザー例外ハンドラーが例外に応答する機会が与えられる前に、デバッガーはOSから通知を受けます。デバッガーがこの(初回)例外通知を処理しないことを選択した場合、例外ディスパッチシーケンスはさらに進み、ターゲットスレッドは、処理する必要がある場合に例外を処理する機会が与えられます。SEH例外がターゲットプロセスで処理されない場合、デバッガーには、セカンドチャンス通知と呼ばれる別のデバッグイベントが送信され、未処理の例外がターゲットプロセスで発生したことを通知します。ソース
2.デバッガーはどのようにしてブレークポイントで停止するかを知っていますか?
簡単な答えは次のとおりです。プログラムにブレークポイントを設定すると、デバッガーはその時点でコードをソフトウェア割り込みであるint3命令に置き換えます。結果として、プログラムが中断され、デバッガーが呼び出されます。
私の理解では、アプリケーションまたはDLLファイルをコンパイルすると、コンパイルされるものにはすべて、関数と変数を表すシンボルが含まれます。
デバッグビルドがある場合、これらのシンボルはリリースビルドの場合よりもはるかに詳細であるため、デバッガーはより多くの情報を提供できます。デバッガーをプロセスにアタッチすると、現在アクセスされている関数が確認され、ここから使用可能なすべてのデバッグシンボルが解決されます(コンパイルされたファイルの内部がどのようになっているのかを知っているため、メモリ内の内容を確認できます) 、int、float、stringなどのコンテンツを含む)。最初のポスターが言ったように、この情報とこれらの記号がどのように機能するかは、環境と言語に大きく依存します。