dbx は動的にリンクされた共有ライブラリのデバッグを完全にサポートしています。ただし、これらのライブラリが -g オプションを使用してインストールされていることが前提になります。
この章の内容は次のとおりです。
動的リンカーは rtld、実行時 ld、または ld.so とも呼ばれ、実行中のアプリケーションに共有オブジェクト (ロードオブジェクト) を組み込むように準備します。rtld が稼働状態になるのは主に次の 2 つの場合です。
プログラムの起動時 - プログラムの起動時、rtld はまずリンク時に指定されたすべての共有オブジェクトを動的に読み込みます。これらは「あらかじめ読み込まれた」共有オブジェクトで、一般に libc.so、libC.so、libX.so などがあります。ldd (1) を使用すれば、プログラムによって読み込まれる共有オブジェクトを調べることができます。
アプリケーションから呼び出しがあった場合 - アプリケーションでは、関数呼び出し dlopen(3) と dlclose(3) を使用して共有オブジェクトやプレーンな実行可能ファイルの読み込みや読み込みの取り消しを行います。
共有オブジェクト (.so) や通常の実行可能ファイル (a.out) のことを、dbx では「ロードオブジェクト」といいます。loadobject コマンド (「loadobject コマンド」参照) を使用して、読み込みオブジェクトからの記号情報のリストの作成および管理ができます。
動的リンカーは、読み込んだすべてのオブジェクトのリストを、link map というリストで管理します。 このリストは、デバッグするプログラムのメモリーに保存され、librtld_db.so で間接的にアクセスできます。これはデバッガ用に用意された特別なシステムライブラリです。
.init セクションは、共有オブジェクトの読み込み時に実行される、その共有オブジェクトのコードの一部分です。たとえば、.init セクションは、C++ 実行時システムが .so 内のすべての静的初期化関数を呼び出すときに使用します。
動的リンカーは最初にすべての共有オブジェクトにマップインし、それらのオブジェクトをリンクマップに登録します。その後、動的リンカーはリンクマップをトラバースし、各共有オブジェクトに対して .init セクションを実行します。syncrtld イベント (「syncrtld」参照) は、これら 2 つの動作の間に発生します。
PLT は、共有オブジェクトの境界間の呼び出しを容易にするために rtld によって使 用される構造体です。たとえば、printf の呼び出しはこの間接テーブルによって行います。その方法の詳細については、SVR4 ABI に関する汎用リファレンスマニュアルおよびプロセッサ固有のリファレンスマニュアルを参照してください。
複数の PLT 間で step コマンドと next コマンドを操作するために、dbx は各ロードオブジェクトの PLT テーブルを追跡する必要があります。テーブル情報は rtld ハンドシェークと同時に入手されます。
dlopen() で読み込んだ共有オブジェクトに fix と cont を使用する場合、開き方を変更しないと fix と cont が正しく機能しません。モード RTLD_NOW | RTLD_GLOBAL または RTLD_LAZY | RTLD_GLOBAL を使用します。
共有ライブラリにブレークポイントを設定する場合、dbx はプログラムの実行時にそのライブラリが使用されることを知っている必要があります。また、そのライブラリのシンボルテーブルを読み込む必要もあります。新しく読み込まれたプログラムが実行時に使用するライブラリを調べる際、dbx は実行時リンカーが起動時のライブラリのすべてを読み込むのに十分な時間を使い、プログラムを実行します。そして、読み込まれたライブラリのリストを読み取ってプロセスを終了します。このとき、ライブラリは読み込まれたままであるため、デバッグ対象としてプログラムを再実行する前にそれらのライブラリにブレークポイントを設定することができます。
dbx は、3 つあるうちのどの方法 (コマンド行から dbx コマンドを使用、dbx プロンプトで debug コマンドを使用、IDE で dbx デバッガを使用) でプログラムが読み込まれたかに関係なく、同じ手順に従ってライブラリを読み込みます。
dbx は dlopen() または dlclose() の発生を自動的に検出し、読み込まれたオブジェクトの記号テーブルを読み込みます。dlopen() で共有オブジェクトを読み込むと、そのオブジェクトにブレークポイントを設定できます。またプログラムのその他の任意の場所で行う場合と同様にデバッグも可能です。
共有オブジェクトを dlclose() で読み込み解除しても、dbx はそのオブジェクトに設定されていたブレークポイントを記憶しているので、たとえアプリケーションを再実行しても、共有オブジェクトが dlopen() で再び読み込まれれば再びそのブレークポイントを設定し直します。
ただし、dlopen() で共有オブジェクトが読み込まれるのを待たなくても共有オブジェクトにブレークポイントを設定したり、その関数やソースコードを検索することはできます。デバッグするプログラムが dlopen() で読み込む共有オブジェクトの名前がわかっていれば、loadobject -load コマンドを使用してその記号テーブルをあらかじめ dbx に読み込んでおくことができます。
loadobject -load /usr/java1.1/lib/libjava_g.so |
これで、dlopen() で読み込む前でも、この読み込みオブジェクト内でモジュールと関数を検索してその中にブレークポイントを設定できます。読み込みオブジェクトの読み込みが済んだら、dbx はブレークポイントを自動的に設定します。
動的にリンクしたライブラリにブレークポイントを設定する場合、次の制約があります。
dlopen() で読み込んだ「フィルタ」ライブラリには、その中の最初の関数が呼び出されるまでブレークポイントは設定できません。
dlopen() でライブラリを読み込むと、初期化ルーチン _init() が呼び出されます。このルーチンがライブラリ内のほかのルーチンを呼び出すこともあります。この初期化が終了するまで、dbx は読み込んだライブラリにブレークポイントを設定できません。具体的には、dbx は、dlopen で読み込んだライブラリ内の _init() では停止できません。