「rtld-監査」インタフェースを使用すると、プロセスがそれ自身に関する実行時リンク情報にアクセスできます。共有オブジェクトのプロファイリングでは、この機構の使用例として、共有オブジェクトの実行時プロファイルを示します。
「rtld-監査」インタフェースは、1 つまたは複数の監査インタフェースルーチンを提供する監査ライブラリとして実装されます。このライブラリがプロセスの一部として読み込まれている場合は、プロセス実行の各段階で、実行時リンカーによって監査ルーチンが呼び出されます。これらのインタフェースを使用すると、監査ライブラリは次のものにアクセスできます。
依存関係の検索。検索パスは監査ライブラリによって置き換えることができる
読み込まれているオブジェクトに関する情報
読み込まれているこれらのオブジェクト間で発生するシンボル結合。これらの結合は、監査ライブラリによって変更できる
関数呼び出しとその戻り値の監査を可能にするために、プロシージャのリンカーテーブルエントリによって提供される遅延結合機構の開発。関数の引数とその戻り値は、監査ライブラリによって変更できる。プロシージャのリンクテーブル (プロセッサ固有)を参照してください。
これらの機能のいくつかは、特殊な共有オブジェクトを事前に読み込むことによって実現できます。しかし、事前に読み込まれたオブジェクトは、プロセスのオブジェクトと同じ名前空間内に存在します。このため、通常、事前に読み込まれた共有オブジェクトの実装は制限されるか、複雑になります。「rtld-監査」インタフェースは、ユーザーに対して、監査ライブラリを実行するための固有の名前空間を提供します。この名前空間により、監査ライブラリがプロセス内で発生する通常の結合を妨害することはなくなります。
実行時リンカーは、動的実行可能なプログラムをその依存関係と結合すると、リンクマップのリンクリストを生成して、プロセスを記述します。リンクマップ構造は /usr/include/sys/link.h に定義されており、プロセス内の各オブジェクトを記述します。アプリケーションのオブジェクトを結合するために必要な記号検索機構は、このリンクマップリストを検索します。このリンクマップリストは、プロセスシンボル解決用の名前空間を提供します。
実行時リンカー自体も、リンクマップによって記述されます。このリンクマップは、アプリケーションオブジェクトのリストとは異なるリストで管理されます。この結果、実行時リンカーが固有の名前空間内に常駐することになるため、実行時リンカー内のサービスにアプリケーションが直接結合されることはなくなります。アプリケーションは、フィルタ libdl.so.1 を介して、実行時リンカーの公開サービスを呼び出すことができるだけです。
「rtld-監査」インタフェースは、すべての監査ライブラリを保持するための各自のリンクマップリストを使用します。このため、監査ライブラリは、アプリケーションのシンボル結合条件から分離されます。アプリケーションリンクマップリストの検査は、dlmopen(3DL) によって実行できます。dlmopen(3DL) を RTLD_NOLOAD フラグとともに使用すると、 監査ライブラリで、オブジェクトを読み込むことなくその存在を照会することができます。
アプリケーションと実行時リンカーのリンクマップリストを定義するために、2 つの識別子が /usr/include/link.h に定義されています。
#define LM_ID_BASE 0 /* application link-map list */ #define LM_ID_LDSO 1 /* runtime linker link-map list */
各「rtld-監査」サポートライブラリには、固有の新しいリンクマップ識別子が割り当てられています。
監査ライブラリは他の共有オブジェクトと同様に構築されます。プロセス内の固有名前空間には、いくつかの注意が必要です。
ライブラリは、すべての依存関係の条件を提供しなければならない
ライブラリは、プロセス内のインタフェースに複数のインスタンスを提供しないシステムインタフェースを使用できない
監査ライブラリが printf(3C) を呼び出す場合、その監査ライブラリは、libc への依存関係を定義する必要があります。共有オブジェクト出力ファイルの生成を参照してください。監査ライブラリには、固有の名前空間があるため、監査中のアプリケーションに存在する libc によって記号参照を満たすことはできません。監査ライブラリに libc への依存関係がある場合は、2 つのバージョンの libc.so.1 がプロセスに読み込まれます。1 つはアプリケーションのリンクマップリストの結合条件を満たし、もう 1 つは監査リンクマップリストの結合条件を満たします。
すべての依存関係が記録された状態で監査ライブラリが構築されるようにするには、リンカーの -z defs オプションを使用します。
システムインタフェースの中には、自らがプロセス内部の実装の唯一のインスタンスであると想定して存在するものがあります (シグナルや malloc(3C) など)。このようなインタフェースを使用すると、アプリケーションの動作が不正に変更されるおそれがあるため、監査ライブラリでは、このようなインタフェースの使用を避ける必要があります。
監査ライブラリは、mapmalloc(3MALLOC) を使用してメモリー割り当てを行うことができます。これは、アプリケーションによって通常使用される割り当てスキーマとこの割り当てが共存可能なためです。
「rtld-監査」インタフェースは、次のいずれかの方法によって有効になります。それぞれの方法は、監視対象のオブジェクトの範囲を意味します。
「大域」監査は、環境変数 LD_AUDIT
を使用することにより有効になる。 この方法により使用可能になる監査ライブラリには、プロセスが使用するすべての動的オブジェクトに関する情報が指定される
「ローカル」監査は、オブジェクトの作成時にオブジェクト内に記録された動的エントリによって有効になる。この方法によって使用可能になる監査ライブラリには、監査する動的オブジェクトに関する情報が指定される
それぞれの呼び出し方法は、 dlmopen(3DL) によって読み込まれる共有オブジェクトをコロンで区切ったリストを含む文字列で構成されています。各オブジェクトは、各自の監査リンクマップリストに読み込まれます。また、各オブジェクトは、dlsym(3DL) によって、監査ルーチンがないか検索されます。 検出された監査ルーチンは、アプリケーション実行中に各段階で呼び出されます。
「rtld-監査」インタフェースを使用すると、複数の監査ライブラリを指定することができます。この方法で使用される監査ライブラリは、通常実行時リンカーによって返される結合を変更することはできません。これらの結合を変更すると、後に続く監査ライブラリで予期しない結果が生じる場合があります。
安全なアプリケーションは、トラストディレクトリからだけ監査ライブラリを取得できます。デフォルトでは、実行時リンカーが認識する 32 ビットオブジェクト用の唯一のトラストディレクトリは、/usr/lib/secure です。64 ビットオブジェクト用のトラストディレクトリは /usr/lib/secure/64 です。
ローカル監査要求は、オブジェクトがリンカーオプション -p または -P を使用して作成された場合に確立できます。たとえば、監査ライブラリ audit.so.1 を使用して libfoo.so.1 を監査するには、リンク編集時に -p オプションを使用して、この要求を記録します。
$ cc -G -o libfoo.so.1 -Wl,-paudit.so.1 -K pic foo.c $ dump -Lv libfoo.so.1 | fgrep AUDIT [3] AUDIT audit.so.1 |
実行時には、この監査識別子があることにより監査ライブラリが読み込まれ、識別するオブジェクトに関する情報がその監査ライブラリに渡されます。
この仕組みだけでは、識別するオブジェクトの検索などの情報は監査ライブラリが読み込まれる前に発生してしまいます。できるだけ多くの監査情報を提供するため、ローカル監査を要求するオブジェクトの存在は、そのオブジェクトのユーザーに広く知らされます。たとえば、libfoo.so.1 に依存するアプリケーションを作成すると、そのアプリケーションは、その依存関係の監査が必要であることを示すよう認識されます。
$ cc -o main main.c libfoo.so.1 $ dump -Lv main | fgrep AUDIT [5] DEPAUDIT audit.so.1 |
この機構で監査が有効になると、アプリケーションのすべての明示的な依存関係に関する情報が監査ライブラリに渡されます。この依存関係の監査は、リンカーの -P オプションを使用することにより、オブジェクトの作成時に直接記録することもできます。
$ cc -o main main.c -Wl,-Paudit.so.1 $ dump -Lv main | fgrep AUDIT [5] DEPAUDIT audit.so.1 |
環境変数 LD_NOAUDIT
をヌル以外の値に設定すると、実行時に監査を無効にすることができます。
次の関数が「rtld-監査」インタフェースによって提供されており、使用順序に従って記載されています。
アーキテクチャあるいはオブジェクトクラス固有のインタフェースの参照では、説明を簡潔にするため、省略して一般名を使用します。たとえば、la_symbind32() および la_symbind64() は la_symbind() で表します。
この関数は、実行時リンカーと監査ライブラリの間に初期ハンドシェークを提供します。監査ライブラリが読み込まれるためには、このインタフェースが提供されている必要があります。
uint_t la_version(uint_t version);
実行時リンカーは、サポート可能な最新バージョンの「rtld-監査」によって、このインタフェースを呼び出します。監査ライブラリは、このバージョンが十分に使用できるかどうかを確認して、使用する予定のバージョンを返すことができます。このバージョンは、通常、/usr/include/link.h に定義されている LAV_CURRENT です。
監査ライブラリがゼロ、あるいは、実行時リンカーがサポートする「rtld-監査」インタフェースよりも大きなバージョンを返す場合、監査ライブラリは破棄されます。
この関数は、リンク対応付けアクティビティが行われていることを監査プログラムに知らせます。
void la_activity(uintptr_t * cookie, uint_t flags);
cookie は、リンク対応付けの先頭のオブジェクトを指します。flags は、/usr/include/link.h に定義されているものと同じタイプのアクティビティを指します。
LA_ACT_ADD – リンク対応付けリストにオブジェクトが追加される
LA_ACT_DELETE – リンク対応付けリストからオブジェクトが削除される
LA_ACT_CONSISTENT – オブジェクトのアクティビティが完了した
この関数は、オブジェクトの検索を実行することを監査プログラムに知らせます。
char * la_objsearch(const char * name, uintptr_t * cookie, uint_t flags);
name は、検索中のファイルあるいはパス名を指します。cookie は、検索を開始しているオブジェクトを指します。flags は、/usr/include/link.h に定義されている name の出所および作成を示します。
LA_SER_ORIG – 初期検索名。通常は、DT_NEEDED エントリとして記録されたファイル名、あるいは dlmopen(3DL) に与えられた引数を指す
LA_SER_LIBPATH – パス名が LD_LIBRARY_PATH コンポーネントから作成されている
LA_SER_RUNPATH – パス名が「実行パス」コンポーネントから作成されている
LA_SER_DEFAULT – パス名がデフォルトの検索パスコンポーネントから作成されている
LA_SER_SECURE – パスコンポーネントがセキュアオブジェクトに固有である
戻り値は、実行時リンカーが処理を継続する必要がある検索パス名を示します。値 0 は、このパスが無視されることを示しています。検索パスを監視する監査ライブラリは、name を返します。
この関数は、新しいオブジェクトが実行時リンカーによって読み込まれるたびに呼び出されます。
uint_t la_objopen(Link_map * lmp, Lmid_t lmid, uintptr_t * cookie);
lmp は、新しいオブジェクトを記述するリンクマップ構造を提供します。lmid は、オブジェクトが追加されているリンクマップリストを特定します。cookie は、識別子へのポインタを提供します。この識別子は、オブジェクト lmp に初期設定されます。この識別子は、監査ライブラリによって、オブジェクトをほかの「rtld-監査」インタフェースルーチンに対して特定するように変更できます。
la_objopen() 関数は、このオブジェクトで問題になるシンボル結合を示す値を返します。これらの値により、後の la_symbind() 呼び出しが生じます。この結果の値は、/usr/include/link.h に定義された次の値のマスクです。
LA_FLG_BINDTO – このオブジェクトに対する監査シンボル結合
LA_FLG_BINDFROM – このオブジェクトからの監査シンボル結合
これらの 2 つのフラグの使用法については、la_symbind() 関数を参照してください。
ゼロの戻り値は、結合情報がこのオブジェクトで問題にならないことを示します。
この関数は、すべてのオブジェクトがアプリケーションに読み込まれた後で、アプリケーションへの制御の譲渡が発生する前に一度呼び出されます。
void la_preinit(uintptr_t * cookie);
cookie は、プロセスを開始したプライマリオブジェクト、通常は動的実行可能プログラムを表します。
この関数は、la_objopen() によって結合通知のタグが付けられた 2 つのオブジェクト間で結合が発生すると呼び出されます。
uintptr_t la_symbind32(Elf32_Sym * sym, uint_t ndx, uintptr_t * refcook, uintptr_t * defcook, uint_t * flags); uintptr_t la_symbind64(Elf64_Sym * sym, uint_t ndx, uintptr_t * refcook, uintptr_t * defcook, uint_t * flags, const char * sym_name);
sym は構築された記号構造であり、 sym->st_value は結合された記号定義のアドレスを示します。/usr/include/sys/elf.h を参照してください。la_symbind32() は、sym->st_name を調整して実際の記号名を指すようにしています。la_symbind64() は sym->st_name を結合オブジェクトの文字列テーブルのインデックスのままにしています。
ndx は、結合オブジェクト動的記号テーブル内の記号インデックスを示します。refcook は、この記号への参照を行うオブジェクトを記述します。この識別子は、LA_FLG_BINDFROM を返した la_objopen() 関数に渡されたものと同じです。defcook は、この記号を定義するオブジェクトを記述します。この識別子は、LA_FLG_BINDTO を返した la_objopen() に渡されるものと同じです。
flags は、結合に関する情報を伝達できるデータ項目を指します。このデータ項目を使用すると、プロシージャのリンクテーブルシンボルエントリの連続監査を変更できます。この値は、/usr/include/link.h に定義された次のフラグのマスクです。
LA_SYMB_NOPLTENTER – la_pltenter() 関数は、この記号に対しては呼び出されない
LA_SYMB_NOPLTEXIT – la_pltexit() 関数は、この記号に対しては呼び出されない
LA_SYMB_DLSYM – dlsym(3DL) を呼び出した結果発生したシンボル結合
LA_SYMB_ALTVALUE (LAV_VERSION2) – la_symbind() への以前の呼び出しによって、記号値に対して代替値が返される
デフォルトでは、la_pltenter() または la_pltexit() 関数が存在する場合、これらの関数は、シンボルが参照されるたびに、la_symbind() 関数の後で、プロシージャのリンクテーブルエントリに対して呼び出されます。詳細は、監査インタフェースの制限を参照してください。
戻り値は、この呼び出しに続いて制御を渡す必要があるアドレスを示します。シンボル結合を監視するだけの監査ライブラリは、sym->st_value の値を返すため、制御は結合記号定義に渡されます。監査ライブラリは、異なる値を返すことによって、シンボル結合を意図的にリダイレクトできます。
sym_name は、la_symbind64() のみに適用可能であり、処理されるシンボルの名前を含みます。この名前は、32 ビットインタフェースから sym->st_name フィールドで使用できます。
これらの関数はそれぞれ SPARC および x86 システムで呼び出されます。これらの関数は、結合通知のタグが付いた 2 つのオブジェクト間でプロシージャのリンクテーブルエントリが呼び出されるときに呼び出されます。
uintptr_t la_sparcv8_pltenter(Elf32_Sym * sym, uint_t ndx, uintptr_t * refcook, uintptr_t * defcook, La_sparcv8_regs * regs, uint_t * flags); uintptr_t la_sparcv9_pltenter(Elf64_Sym * sym, uint_t ndx, uintptr_t * refcook, uintptr_t * defcook, La_sparcv9_regs * regs, uint_t * flags, const char * sym_name); uintptr_t la_i86_pltenter(Elf32_Sym * sym, uint_t ndx, uintptr_t * refcook, uintptr_t * defcook, La_i86_regs * regs, uint_t * flags);
sym、ndx、refcook、defcook、および sym_name は、la_symbind() に渡されたものと同じ情報を提供します。
regs は、/usr/include/link.h に定義されているように、SPARC システム上の out レジスタと、x86 システム上の stack および frame レジスタを指します。
flags は、結合に関する情報を伝達できるデータ項目を指します。このデータ項目を使用すると、プロシージャのリンクテーブルシンボルエントリの連続監査を変更できます。このデータ項目は、la_symbind() から flags によって指されるものと同じです。この値は、/usr/include/link.h に定義された次のフラグのマスクです。
LA_SYMB_NOPLTENTER – la_pltenter() は、この記号では再び呼び出されることはない
LA_SYMB_NOPLTEXIT – la_pltexit() は、この記号では呼び出されない
戻り値は、この呼び出しに続いて制御を渡す必要があるアドレスを示します。シンボル結合を監視するだけの監査ライブラリは、sym->st_value の値を返すため、制御は結合記号定義に渡されます。監査ライブラリは、異なる値を返すことによって、シンボル結合を意図的にリダイレクトできます。
この関数は、結合通知のタグが付いた 2 つのオブジェクト間でプロシージャのリンクテーブルエントリが返されるときに呼び出されます。この関数は、制御が呼び出し側に到達する前に呼び出されます。
uintptr_t la_pltexit(Elf32_Sym * sym, uint_t ndx, uintptr_t * refcook, uintptr_t * defcook, uintptr_t retval); uintptr_t la_pltexit64(Elf64_Sym * sym, uint_t ndx, uintptr_t * refcook, uintptr_t * defcook, uintptr_t retval, const char * sym_name);
sym、ndx、refcook、defcook、および sym_name は、la_symbind() に渡されたものと同じ情報を提供します。retval は結合関数からの戻りコードです。シンボル結合を監視する監査ライブラリは、retval を返します。監査ライブラリは、意図的に異なる値を返すことができます。
la_pltexit() は実験段階のインタフェースです。詳細は、監査インタフェースの制限を参照してください。
この関数はオブジェクトに対する終了コードが実行されてから、オブジェクトが読み込みを解除されるまでに呼び出されます。
uint_t la_objclose(uintptr_t * cookie);
cookie はオブジェクトを特定するもので、以前の la_objopen() から取得されています。戻り値は、ここではすべて無視されます。
次の単純な例では、動的実行可能プログラム date(1) によって読み込まれた各共有オブジェクトの依存関係の名前を出力する、監査ライブラリを作成しています。
$ cat audit.c #include <link.h> #include <stdio.h> uint_t la_version(uint_t version) { return (LAV_CURRENT); } uint_t la_objopen(Link_map * lmp, Lmid_t lmid, uintptr_t * cookie) { if (lmid == LM_ID_BASE) (void) printf("file: %s loaded\n", lmp->l_name); return (0); } $ cc -o audit.so.1 -G -K pic -z defs audit.c -lmapmalloc -lc $ LD_AUDIT=./audit.so.1 date file: date loaded file: /usr/lib/libc.so.1 loaded file: /usr/lib/libdl.so.1 loaded file: /usr/lib/locale/en_US/en_US.so.2 loaded Thur Aug 10 17:03:55 PST 2000 |
/usr/demo/link_audit の SUNWosdem パッケージには、「rtld-監査」インタフェースを使用する多数のデモアプリケーションが用意されています。
sotruss(1) と whocalls(1) は、SUNWtoo パッケージに組み込まれています。perfcnt と symbindrep はサンプルプログラムです。これらは、実際の環境での使用を目的としていません。
la_pltexit() 系列の使用にはいくつかの制限があります。これらの制限は、呼び出し側と「呼び出し先」の間で余分なスタックフレームを挿入して、la_pltexit() 戻り値を提供するための必要から生じたものです。la_pltenter() ルーチンだけを呼び出す場合、この条件は問題になりません。この場合、目的の関数に制御を渡す前に、余分なスタックを整理できます。
これらの制限が原因で、la_pltexit() は、実験的インタフェースとみなされます。問題がある場合には、la_pltexit() ルーチンの使用は避けてください。
スタックを直接検査するか、またはその状態について仮定をたてる少数の関数があります。これらの関数の例としては、setjmp(3C) ファミリ、vfork(2)、および構造へのポインタではなく構造を返す関数があります。これらの関数は、la_pltexit() をサポートするために作成される余分なスタックによって調整されます。
実行時リンカーは、このタイプの関数を検出できないため、監査ライブラリの作成元が、このようなルーチンの la_pltexit() を無効にする必要があります。