dbx コマンドによるデバッグ

共有オブジェクトのデバッグ

dbx は、あらかじめ読み込まれた共有オブジェクトと dlopen() によって開かれた共有オブジェクトの両方をデバッグできます。デバッグ時のいくつかの制限事項については後述します。

起動手順

あらかじめ読み込まれた共有オブジェクトにブレークポイントを置くためには、ルーチンのアドレスを dbx に知らせる必要があり、dbx がルーチンのアドレスを知るためには、共有オブジェクトのベースアドレスを知らなければなりません。たとえば次のような作業を行うとします。


stop in printf
run

このような単純な作業でも、dbx には特別な配慮が必要です。新しいプログラムが読み込まれるたびに 、dbxrtld がリンクマップの作成を完了した場所までプログラムを自動的に実行します。dbx はリンクマップを読み取り、ベースアドレスを格納します。その後、プロセスは終了し、メッセージとプロンプトが表示されます。dbx は、これらの動作中にメッセージを表示しません。

この時点で、ベース読み込みアドレスとともに libc.so のシンボルテーブルを調べることができるため、printf のアドレスがわかります。

rtld によってリンクマップが作成されるのを待機し、リンクマップの先頭にアクセスするまでの dbx の動作を「rtld ハンドシェーク」と呼びます。rtld がリンクマップを作成し、dbx がすべてのシンボルテーブルを読み取ると、イベント syncrtld が発生します。

この方式では、dbx はプログラムの実行時、各共有ライブラリが同じベースアドレスに読み込まれていることを前提とします。ただし、プログラムを読み込んで実行するまでに LD_LIBRARY_PATH を変更した場合にかぎり、ライブラリは同じアドレスに読み込まれません。この変数が変更された場合、dbx は新しいアドレスのメッセージを出力しますが、変更後の共有オブジェクト内のブレークポイントが正しくない場合があります。

起動手順と .init セクション

.init セクションは、共有オブジェクトの読み込み時に実行される、その共有オブジェクトのコードの一部分です。たとえば、.init セクションは、C++ 実行時システムがすべての静的初期化関数を呼び出すときに使用します。

動的リンカーは最初にすべての共有オブジェクトにマップインし、それらのオブジェクトをリンクマップに登録します。その後、動的リンカーはリンクマップに含まれる各オブジェクトの .init セクションを順に実行していきます。

dlopen() と dlclose()

dbx は、dlopen または dlclose の発生を自動的に検出し、読み込んだオブジェクトのシンボルテーブルを読み込みます。読み込まれたオブジェクトへのブレークポイントの設定やデバッグは、プログラムのほかの部分と同じように行うことができます。

共有オブジェクトの読み込みが取り消されると、シンボルテーブルは破棄され、ブレークポイントは status を要求すると“defunct”(消滅) と報告されます。残念ながら、直後の run 操作でオブジェクトが読み込まれた場合に、ブレークポイントを自動的に有効にする方法はありません。

dlopendlclose の 2 つのイベントを when コマンドやシェルプログラムで使用することにより、dlopen 型の共有オブジェクトでのブレークポイント管理の負荷を軽減できます。

修正継続機能 (fix と continue)

共有オブジェクトに対して fixcontinue を正しく動作させるには、オープンの方法を変更する必要があります。モード 'RTLD_NOW|RTLD_GLOBAL' または 'RTLD_LAZY|RTLD_GLOBAL' を使用してください。

プロシージャ・リンケージ・テーブル (PLT)

PLT は、共有オブジェクトの境界間の呼び出しを容易にするために rtld によって使用される構造体です。たとえば、printf の呼び出しはこの間接テーブルによって行います。その方法の詳細については、SVR4 ABI に関する汎用リファレンスマニュアルおよびプロセッサ固有のリファレンスマニュアルを参照してください。

複数の PLT 間で step コマンドと next コマンドを操作するために、dbx は各ロードオブジェクトの PLT テーブルを追跡する必要があります。テーブル情報は rtld ハンドシェークと同時に入手されます。