Go to main content
Oracle® Solaris 11.3 リンカーとライブラリガイド

印刷ビューの終了

更新: 2015 年 10 月
 
 

実行時リンカーの監査インタフェース

rtld-監査インタフェースを使用すると、プロセスの実行時リンクに関する情報にアクセスできます。「rtld-監査」インタフェースは、1 つまたは複数の監査インタフェースルーチンを提供する監査ライブラリとして実装されます。このライブラリがプロセスの一部として読み込まれている場合は、プロセス実行の各段階で、実行時リンカーによって監査ルーチンが呼び出されます。監査ライブラリはこれらのインタフェースを使って、次の情報にアクセスできます。

  • 依存関係の検索。検索パスは監査ライブラリによって置き換えることができます。

  • 読み込まれているオブジェクトに関する情報。

  • 読み込まれているこれらのオブジェクト間で発生するシンボル結合。これらの結合は、監査ライブラリによって変更できます。

  • プロシージャーのリンクテーブルエントリによって提供される遅延結合メカニズム。関数呼び出しとその戻り値を監査できます。プロシージャーのリンクテーブル (プロセッサ固有)を参照してください。関数の引数とその戻り値は、監査ライブラリによって変更できます。

この情報のいくつかは、特殊な共有オブジェクトを事前に読み込むことによって取得できます。しかし、事前に読み込まれたオブジェクトは、アプリケーションのオブジェクトと同じ名前空間内に存在します。このため、通常、事前に読み込まれた共有オブジェクトの実装は制限されるか、複雑になります。rtld-監査インタフェースは、ユーザーに監査ライブラリを実行するための固有の名前空間を提供します。この名前空間により、監査ライブラリがアプリケーション内で発生する通常の結合を妨害することはなくなります。

rtld-監査インタフェースの使用例として、共有オブジェクトのプロファイリングで説明した共有オブジェクトの実行時プロファイリングがあります。

名前空間の確立

実行時リンカーは、動的実行可能なプログラムをその依存関係と結合すると、リンクマップのリンクリストを生成して、アプリケーションを記述します。リンクマップ構造は、アプリケーション内の各オブジェクトを記述します。リンクマップ構造は、/usr/include/sys/link.h に定義されています。アプリケーションのオブジェクトの結合に必要なシンボル検索メカニズムは、このリンクマップリスト全体を検索します。このリンクマップリストは、アプリケーションシンボル解決用の名前空間を提供します。

実行時リンカーも、リンクマップによって記述されます。このリンクマップは、アプリケーションオブジェクトのリストとは異なるリストで管理されます。この結果、実行時リンカーは固有の名前空間に存在するため、アプリケーションは実行時リンカー内のサービスを参照したり、直接アクセスしたりできません。したがって、アプリケーションは libc.so.1 または libdl.so.1 が提供するフィルタを介してのみ実行時リンカーにアクセスできます。

アプリケーションと実行時リンカーのリンクマップリストを定義するために、2 つの識別子が /usr/include/link.h に定義されています。

#define LM_ID_BASE      0     /* application link-map list */
#define LM_ID_LDSO      1     /* runtime linker link-map list */

実行時リンカーを使用すると、これら 2 つの標準リンクマップリストに加えて、任意の数の追加リンクマップリストを作成できます。これらの追加リンクマップリストでは、それぞれに固有の名前空間が提供されます。rtld 監査インタフェースは、監査ライブラリを保持するために独自のリンクマップリストを使用します。このため、監査ライブラリは、アプリケーションのシンボル結合要件から分離されます。すべての「rtld-監査」サポートライブラリには、固有の新しいリンクマップ識別子が割り当てられています。

監査ライブラリでは、dlmopen(3C) を使用するとアプリケーションのリンクマップリストを検査できます。RTLD_NOLOAD フラグを指定して dlmopen() を使用すると、監査ライブラリはオブジェクトを読み込まずにそのオブジェクトの有無を問い合わせることができます。

監査ライブラリの作成

監査ライブラリはほかの共有オブジェクトと同様に構築されます。ただし、プロセス内の監査ライブラリに固有の名前空間には、いくつかの注意が必要です。

  • ライブラリは、すべての依存関係の要件を提供しなければならない。

  • ライブラリは、プロセス内のインタフェースに複数のインスタンスを提供しないシステムインタフェースを使用できない。

監査ライブラリが外部インタフェースを参照している場合、監査ライブラリではインタフェースを定義する依存関係を定義する必要があります。たとえば、監査ライブラリが printf(3C) を呼び出す場合、監査ライブラリは libc への依存関係を定義する必要があります。共有オブジェクト出力ファイルの生成を参照監査ライブラリには、固有の名前空間があるため、監査中のアプリケーションに存在する libc によってシンボル参照を満たすことはできません。監査ライブラリに libc への依存関係がある場合は、2 つのバージョンの libc.so.1 がプロセスに読み込まれます。1 つはアプリケーションのリンクマップリストの結合要件を満たし、もう 1 つは監査リンクマップリストの結合要件を満たします。

すべての依存関係が記録された状態で監査ライブラリが構築されるようにするには、リンカーの –z defs オプションを使用します。

システムインタフェースの中には、自らがプロセス内部の実装の唯一のインスタンスであると想定しているものがあります。このような実装の例として、シグナルおよび malloc(3C) があります。このようなインタフェースを使用すると、アプリケーションの動作が不正に変更されるおそれがあるため、監査ライブラリでは、このようなインタフェースの使用を避ける必要があります。


注 - 監査ライブラリは、mapmalloc(3MALLOC) を使用してメモリー割り当てを行うことができます。これは、アプリケーションによって通常使用される割り当てスキームとこの割り当てが共存可能なためです。

監査インタフェースの呼び出し

「rtld-監査」インタフェースは、次のいずれかの方法によって有効になります。それぞれの方法は、監視対象のオブジェクトの範囲を意味します。

  • ローカル監査は、オブジェクトの作成時に 1 つまたは複数の監査プログラムを定義することで有効になります。ローカル監査の記録を参照してください。この方法で実行時に使用可能になる監査ライブラリには、ローカル監査を要求した動的オブジェクトに関する情報が提供されます。

  • 大域監査は、環境変数 LD_AUDIT を使用して 1 つまたは複数の監査プログラムを定義することで有効になります。また、ローカル監査定義と –z globalaudit オプションを組み合わせることによっても、アプリケーションの大域監査を有効にすることができます。大域監査の記録を参照してください。これらの方法により実行時に使用可能になる監査ライブラリには、アプリケーションが使用するすべての動的オブジェクトに関する情報が提供されます。

監査プログラムを定義する両方のメソッドでは、dlmopen(3C) によってロードされる、コロン区切りの共有オブジェクトリストから構成される文字列を使用します。各オブジェクトは、各自の監査リンクマップリストに読み込まれます。また、各オブジェクトは、dlsym(3C) によって、監査ルーチンがないか検索されます。検出された監査ルーチンは、アプリケーション実行中に各段階で呼び出されます。

安全なアプリケーションは、トラストディレクトリからだけ監査ライブラリを取得できます。デフォルトでは、32 ビットオブジェクトの実行時リンカーが認識できるトラストディレクトリは、/lib/secure/usr/lib/secure だけです。64 ビットオブジェクトの場合、トラストディレクトリは /lib/secure/64/usr/lib/secure/64 です。


注 -  環境変数 LD_NOAUDIT をヌル以外の値に設定すると、実行時に監査を無効にすることができます。

ローカル監査の記録

ローカル監査要求は、オブジェクトがリンカーオプション –p または –P を使用して作成された場合に確立できます。たとえば、監査ライブラリ audit.so.1 を使用して libfoo.so.1 を監査するには、リンク編集時に –p オプションを使用して、この要求を記録します。

$ cc -G -o libfoo.so.1 -Wl,-paudit.so.1 -K pic foo.c
$ elfdump -d libfoo.so.1 | grep AUDIT
     [2]  AUDIT             0x96                audit.so.1

実行時には、この監査識別子があることにより監査ライブラリが読み込まれます。次に、識別するオブジェクトに関する情報がその監査ライブラリに渡されます。

このメカニズムだけでは、識別するオブジェクトの検索などの情報は監査ライブラリが読み込まれる前に発生します。できるだけ多くの監査情報を提供するため、ローカル監査を要求するオブジェクトの存在は、そのオブジェクトのユーザーに広く知らされます。たとえば、libfoo.so.1 に依存するアプリケーションを作成すると、そのアプリケーションは、その依存関係の監査が必要であることを示すよう認識されます。

$ cc -o main main.c libfoo.so.1
$ elfdump -d main | grep AUDIT
     [4]  DEPAUDIT          0x1be               audit.so.1

このメカニズムで監査が有効になると、アプリケーションのすべての明示的な依存関係に関する情報が監査ライブラリに渡されます。この依存関係の監査は、リンカーの –P オプションを使用することにより、オブジェクトの作成時に直接記録することもできます。

$ cc -o main main.c -Wl,-Paudit.so.1
$ elfdump -d main | grep AUDIT
     [3]  DEPAUDIT          0x1b2               audit.so.1

大域監査の記録

大域監査の要件は、環境変数 LD_AUDIT を設定することによって確立できます。たとえば、この環境変数を使用すると、アプリケーションの main とそのアプリケーションのすべての依存関係を、監査ライブラリ audit.so.1 を使って監査できます。

$ LD_AUDIT=audit.so.1 main

また、–z globalaudit オプションを指定することで、アプリケーション内のローカル監査を記録することによる大域監査を実現できます。たとえば、大域監査が有効になるようにアプリケーション main を構築するには、リンカーの –P オプションと –z globalaudit オプションを使用します。

$ cc -o main main.c -Wl,-Paudit.so.1 -z globalaudit
$ elfdump -d main | grep AUDIT
     [3]  DEPAUDIT          0x1b2               audit.so.1
    [26]  FLAGS_1       0x1000000              [ GLOBAL-AUDITING ]

監査がこれらのメカニズムのどちらで有効化された場合も、アプリケーションのすべての動的オブジェクトに関する情報が監査ライブラリに渡されます。

監査インタフェースの対話

監査ルーチンには 1 つまたは複数の cookie が設定されます。cookie とは、個々の動的オブジェクトを記述するデータ項目です。最初の cookie が la_objopen() ルーチンに設定されるのは、動的オブジェクトが最初に読み込まれたときです。この cookie は、読み込まれた動的オブジェクトの関連した Link_map に対するポインタです。しかし、la_objopen() ルーチンは自由に別の cookie を割り当てたり、実行時リンカーに返したりできます。このメカニズムにより、監査プログラムは各動的オブジェクトとともに独自のデータを保持し、後続のすべての監査ルーチン呼び出しでこのデータを受け取れます。

「rtld-監査」インタフェースを使用すると、複数の監査ライブラリを指定することができます。この場合、ある監査プログラムからの戻り情報は次の監査プログラムの同じ監査ルーチンに渡されます。同様に、ある監査プログラムが作成した cookie は、次の監査プログラムに渡されます。ほかの監査ライブラリとの共存が想定される監査ライブラリを設計する際には注意が必要です。安全に実行するために、実行時リンカーによって通常返される結合または cookie を変更しないでください。これらのデータを変更すると、後に続く監査ライブラリで予期しない結果が生じる場合があります。変更する場合、結合または cookie の情報を変更しても監査プログラムが安全に連携できるように、監査プログラムを設計する必要があります。

監査インタフェースの関数

次のルーチンが rtld-監査インタフェースによって提供されます。これらのルーチンは予想される使用順序に従って記載されています。


注 - アーキテクチャーあるいはオブジェクトクラス固有のインタフェースの参照では、説明を簡潔にするため、省略して一般名を使用します。たとえば、la_symbind32() および la_symbind64()la_symbind() で表します。
la_version()

このルーチンは、実行時リンカーと監査ライブラリの間の初期ハンドシェークを提供します。監査ライブラリが読み込まれるためには、このインタフェースが提供されている必要があります。

uint_t la_version(uint_t version);

実行時リンカーは、実行時リンカーがサポート可能な最上位バージョンの「rtld-監査」インタフェースによって、このインタフェースを呼び出します。監査ライブラリは、このバージョンが使用するのに十分かどうかを確認して、監査ライブラリが使用する予定のバージョンを返すことができます。このバージョンは、通常、/usr/include/link.h に定義されている LAV_CURRENT です。

監査ライブラリがゼロ、あるいは、実行時リンカーがサポートする「rtld-監査」インタフェースよりも大きなバージョンを返す場合、監査ライブラリは破棄されます。

残りの監査ルーチンには 1 つ以上の cookie が渡されます。監査インタフェースの対話を参照してください。

la_version() の呼び出しに続いて、la_objopen() ルーチンが 2 回呼び出されます。最初の呼び出しでは動的実行可能ファイルのリンクマップ情報が渡され、2 回目の呼び出しでは実行時リンカーのリンクマップ情報が渡されます。

la_objopen()

このルーチンは、新しいオブジェクトが実行時リンカーによって読み込まれるときに呼び出されます。

uint_t la_objopen(Link_map *lmp, Lmid_t lmid, uintptr_t *cookie);

lmp は、新しいオブジェクトを記述するリンクマップ構造を提供します。lmid は、オブジェクトが追加されているリンクマップリストを特定します。cookie は、識別子へのポインタを提供します。この識別子は、オブジェクト lmp に初期設定されます。オブジェクトをほかの rtld-監査インタフェースルーチンで識別しやすくするために、監査ライブラリでこの識別子を再割り当てすることができます。

la_objopen() ルーチンは、このオブジェクトで関心があるシンボル結合を示す値を返します。この結果の値は、/usr/include/link.h に定義された次の値のマスクです。

  • LA_FLG_BINDTO – このオブジェクトに対する監査シンボル結合。

  • LA_FLG_BINDFROM – このオブジェクトからの監査シンボル結合。

これらの値により、監査者は la_symbind() でモニターするオブジェクトを選択できます。ゼロの戻り値は、結合情報がこのオブジェクトで問題にならないことを示します。

たとえば、監査者は、libfoo.so から libbar.so への結合をモニターできます。libfoo.so()la_objopen は、 LA_FLG_BINDFROM を返します。libbar.sola_objopen() は、LA_FLG_BINDTO を返します。

監査者は、libfoo.solibbar.so 間のすべての結合をモニターできます。両方のオブジェクトの la_objopen() は、LA_FLG_BINDFROM LA_FLG_BINDTO を返します。

監査者は、libbar.so へのすべての結合もモニターできます。libbar.sola_objopen() は、LA_FLG_BINDTO を返します。すべての la_objopen() 呼び出しは、LA_FLG_BINDFROM を返します。

監査バージョン LAV_VERSION5 を指定すると、動的実行可能ファイルを表す la_objopen() 呼び出しがローカル監査プログラムで行われます。この場合、監査プログラムはシンボル結合フラグを返さないでください。モニタープログラムの読み込みが遅すぎるために、動的実行可能ファイルに関連するシンボル結合をモニターできない場合があるためです。監査プログラムによって返されるフラグはすべて無視されます。la_objopen() 呼び出しは、ローカル監査プログラムに、後続の la_preinit() または la_activity() 呼び出しに必要な初期 cookie を提供します。

la_activity()

このルーチンは、リンクマップアクティビティーが行われていることを監査プログラムに知らせます。

void la_activity(uintptr_t *cookie, uint_t flags);

cookie は、リンクマップの先頭のオブジェクトを指します。flags は、/usr/include/link.h に定義されているものと同じタイプのアクティビティーを指します。

  • LA_ACT_ADD – リンクマップリストにオブジェクトが追加される。

  • LA_ACT_DELETE – リンクマップリストからオブジェクトが削除される。

  • LA_ACT_CONSISTENT – オブジェクトのアクティビティーが完了した。

動的実行可能ファイルおよび実行時リンカーの la_objopen() 呼び出しに続いて、LA_ACT_ADD アクティビティーがプロセス起動時に呼び出されて、新しい依存関係が追加されることが示されます。このアクティビティーは、遅延読み込みと dlopen(3C) イベントの場合にも呼び出されます。LA_ACT_DELETE アクティビティーは、dlclose(3C) でオブジェクトが削除されたときにも呼び出されます。

LA_ACT_ADD および LA_ACT_DELETE アクティビティーは、後に続くことが予想されるイベントのヒントです。実際のイベントが異なる場合が多数あります。たとえば、新しいオブジェクトが追加された場合にそれらのオブジェクトを完全に再配置できないときは、新しいオブジェクトのいくつかが削除されることがあります。.fini 実行可能ファイルによって新しいオブジェクトが遅延読み込みになった場合、オブジェクトの削除によって新しいオブジェクトが追加されることもあります。オブジェクトの追加または削除の後に、アプリケーションのリンクマップリストが一貫していることを示すために LA_ACT_CONSISTENT アクティビティーが発生します。このアクティビティーは信頼できます。監査プログラムは、無条件に LA_ACT_ADD および LA_ACT_DELETE のヒントを信じるのではなく、実際の結果を注意深く検証する必要があります。

監査バージョン LAV_VERSION1 から LAV_VERSION4 の場合、la_activity() は大域監査でのみ呼び出されていました。監査バージョンが LAV_VERSION5 の場合、ローカル監査によってアクティビティーのイベントを取得できます。アクティビティーのイベントは、アプリケーションのリンクマップを表す cookie を提供します。このアクティビティーを準備し、監査プログラムがこの cookie の内容を制御できるようにするため、最初にローカル監査の la_objopen() が呼び出されます。la_objopen() 呼び出しによって、アプリケーションのリンクマップを表す初期 cookie が提供されます。監査インタフェースの対話を参照してください。

la_objsearch()

このルーチンは、オブジェクトの検索を実行することを監査プログラムに知らせます。

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 エントリとして記録されたファイル名、あるいは dlopen(3C) に与えられた引数を指します。

  • LA_SER_LIBPATH – パス名が LD_LIBRARY_PATH コンポーネントから作成されている。

  • LA_SER_RUNPATH – パス名が「実行パス」コンポーネントから作成されている。

  • LA_SER_DEFAULT – パス名がデフォルトの検索パスコンポーネントから作成されている。

  • LA_SER_CONFIG – パスコンポーネントの出所が構成ファイルである。crle(1) のマニュアルページを参照してください。

  • LA_SER_SECURE – パスコンポーネントがセキュアなオブジェクトに固有である。

戻り値は、実行時リンカーが処理を継続する必要がある検索パス名を示します。値 0 は、このパスが無視されることを示しています。検索パスをモニターする監査ライブラリは、name を返します。

la_objfilter()

このルーチンは、フィルタが新しいフィルティーを読み込むと呼び出されます。フィルタとしての共有オブジェクトを参照してください。

int la_objfilter(uintptr_t *fltrcook, const char *fltestr,
    uintptr_t *fltecook, uint_t flags);

fltrcook は、フィルタを特定します。fltestr は、フィルティー文字列を指します。fltecook は、フィルティーを特定します。flags は、現在使用されていません。la_objfilter() は、フィルタとフィルティーの la_objopen() が呼び出されたあとに呼び出されます。

戻り値 0 は、このフィルティーが無視されることを示しています。フィルタの使用をモニターする監査ライブラリは、0 以外の値を返します。

la_preinit()

このルーチンは、アプリケーションのすべての即時依存関係がロードされたあとで一度呼び出されます。

void la_preinit(uintptr_t *cookie);

cookie は、プロセスを開始したプライマリオブジェクト、通常は動的実行可能プログラムを表します。

la_preinit() が呼び出されたあとも、プロセスでは、初期スレッドローカルストレージの作成などスレッドの初期化が必要です。プログラムの起動を参照してください。さらに、ロードされたすべてのオブジェクトを実行する前に、その初期設定セクションで収集およびソートを行う必要があります。初期設定および終了ルーチンを参照してください。この関数は、初期プロセスにオブジェクトを追加するときに便利な制御ポイントをもたらします。これらのオブジェクトは、初期スレッドローカルストレージと、プロセスの初期設定に使用できます。

監査バージョン LAV_VERSION1 から LAV_VERSION4 の場合、la_preinit() は大域監査でのみ呼び出されていました。監査バージョンが LAV_VERSION5 の場合、ローカル監査によって preinit イベントを取得できます。preinit イベントは、アプリケーションのリンクマップを表す cookie を提供します。この preinit を準備し、監査プログラムがこの cookie の内容を制御できるようにするため、最初にローカル監査の la_objopen() が呼び出されます。la_objopen() 呼び出しによって、アプリケーションのリンクマップを表す初期 cookie が提供されます。監査インタフェースの対話を参照してください。

la_callinit()

このルーチンは、スレッドの初期化が完了し、初期スレッドローカルストレージが確立されたあとで呼び出されます。さらに、すべての初期化ルーチンは収集およびソートされ、実行できるようになっています。

void la_callinit(uintptr_t *cookie);

cookie と、大域監査およびローカル監査からの呼び出しについては、la_preinit() の場合と同じです。

このインタフェースは、監査バージョン LAV_VERSION6 で追加されたインタフェースであり、アプリケーションコードの実行への遷移にマークを付けます。

la_callentry()

このルーチンは、すべての初期化ルーチンが実行されたあとで呼び出されます。

void la_callentry(uintptr_t *cookie);

cookie と、大域監査およびローカル監査からの呼び出しについては、la_preinit() の場合と同じです。

このインタフェースは、監査バージョン LAV_VERSION6 で追加されたインタフェースであり、アプリケーションエントリポイントへの遷移にマークを付けます。

la_symbind()

このルーチンは、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_symbind() に提供される場合もあります。

  • LA_SYMB_DLSYMdlsym(3C) を呼び出した結果、シンボル結合が発生した。

  • LA_SYMB_ALTVALUE (LAV_VERSION2)la_symbind() への以前の呼び出しによって、シンボル値に対して代替値が返された。

la_pltenter() または la_pltexit() ルーチンが存在する場合、これらのルーチンは、プロシージャーリンクテーブルエントリの la_symbind() の後に呼び出されます。これらのルーチンは、シンボルが参照されるたびに呼び出されます。詳細は、監査インタフェースの制限を参照してください。

次のフラグは、デフォルトの動作を変更するために la_symbind() から提供されます。これらのフラグは、flags 引数が指す値とのビット単位の OR 演算として適用されます。

  • LA_SYMB_NOPLTENTER – このシンボルに対して la_pltenter() ルーチンを呼び出さない

  • LA_SYMB_NOPLTEXIT – このシンボルに対して la_pltexit() ルーチンを呼び出さない

戻り値は、この呼び出しに続いて制御を渡す必要があるアドレスを示します。シンボル結合をモニターするだけの監査ライブラリは、sym->st_value の値を返すため、制御は結合シンボル定義に渡されます。監査ライブラリは、異なる値を返すことによって、シンボル結合を意図的にリダイレクトできます。

sym_name は、la_symbind64() のみに適用可能であり、処理されるシンボルの名前を含みます。この名前は、32 ビットインタフェースの sym->st_name フィールドから得られます。

la_pltenter()

これらのルーチンはシステムに固有です。これらのルーチンは、結合通知のタグが付いた 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);

uintptr_t la_amd64_pltenter(Elf64_Sym *sym, uint_t ndx,
    uintptr_t *refcook, uintptr_t *defcook,
    La_amd64_regs *regs, uint_t *flags, const char *sym_name);

symndxrefcookdefcook、および sym_name は、la_symbind() に渡されたものと同じ情報を提供します。

la_sparcv8_pltenter()la_sparcv9_pltenter() では、regs は out レジスタを指します。la_i86_pltenter() では、regs は stack および frame レジスタを指します。la_amd64_pltenter() では、 regs は stack および frame レジスタ、および整数引数の受け渡しに使用されるレジスタを指します。regs/usr/include/link.h に定義されています。

flags は、結合に関する情報を伝達できるデータ項目を指します。このデータ項目を使用すると、プロシージャーリンクテーブルのエントリの連続監査を変更できます。このデータ項目は、la_symbind() から flags によって指されるものと同じです。

次のフラグは、現在の監査動作を変更するために la_pltenter() から提供できます。これらのフラグは、flags 引数が指す値とのビット単位の OR 演算として適用されます。

  • LA_SYMB_NOPLTENTERla_pltenter() は、このシンボルでは再び呼び出されることはない。

  • LA_SYMB_NOPLTEXITla_pltexit() は、このシンボルでは呼び出されない。

戻り値は、この呼び出しに続いて制御を渡す必要があるアドレスを示します。シンボル結合をモニターするだけの監査ライブラリは、sym->st_value の値を返すため、制御は結合シンボル定義に渡されます。監査ライブラリは、異なる値を返すことによって、シンボル結合を意図的にリダイレクトできます。

la_pltexit()

このルーチンは、結合通知のタグが付いた 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);

symndxrefcookdefcook、および sym_name は、la_symbind() に渡されたものと同じ情報を提供します。retval は結合関数からの戻りコードです。シンボル結合をモニターする監査ライブラリは、retval を返します。監査ライブラリは、意図的に異なる値を返すことができます。


注 - la_pltexit() は実験段階のインタフェースです。詳細は、監査インタフェースの制限を参照してください。
la_objclose()

このルーチンは、オブジェクトに対する終了コードが実行されてから、オブジェクトが読み込みを解除されるまでに呼び出されます。

uint_t la_objclose(uintptr_t *cookie);

cookie はオブジェクトを特定するもので、以前の la_objopen() から取得されています。戻り値は、ここではすべて無視されます。

監査インタフェース制御フロー

以降のセクションでは、監査ライブラリが各インタフェースで実行できる監査インタフェースルーチンおよびアクションについて説明します。プロセス初期設定に重点が置かれています。これらのルーチンは、プロセスの起動時に提供される大域監査の一般的な場合に呼び出される順序で表示されます。

監査インタフェースは、情報と制御の 2 つのカテゴリに分類されます。

情報インタフェースには、オブジェクト検索、オブジェクトロード、シンボル結合など、実行中のプロセスに関する監査ライブラリ情報が表示されます。さらにこれらのインタフェースでは、監査者は、ロードされたオブジェクトを変更し、将来のシンボル結合イベントについて通知するように設定できます。

制御インタフェースは、監査ライブラリで、プロセス実行内のフェーズの開始または終了を追跡できるようにするために呼び出されます。これらのインタフェースでは、監査者は一貫した一連のオブジェクトを安全に検査でき、新しいオブジェクトをロードすることもできます。

監査ライブラリが最初にロードされると、ライブラリの la_version() インタフェースがすぐに呼び出されます。このハンドシェイクにより、監査ライブラリは、サポートされているか検証され、実行時リンカーから必要とするインタフェースバージョンを定義できます。

監査ライブラリは、LD_AUDIT を使用するか、プロセスを始動する実行可能オブジェクト内のローカル監査定義から、プロセスの始動時に確立できます。監査インタフェースの呼び出しを参照してください。このシナリオでは、実行可能オブジェクトと実行時リンカーの両方の la_objopen() 呼び出しが、監査ライブラリに提供されます。

この時点では、プロセスはまだ構築の初期段階です。監査者は、プロセスへのオブジェクトの追加やプロセスの徹底的なシンボル検索など、この構築を妨げる可能性のあるすべてのアクションを実行しないようにする必要があります。このようなアクションを行うと、シンボル検索に適合しようとしてオブジェクトを早まってロードしたりリロードする結果になる可能性があります。

プロセス初期設定時に即座にロードされる依存関係は、それぞれ監査ライブラリの la_objopen() インタフェースに報告されます。遅延ロードを使用するプロセスの場合、プロセス初期設定時にはわずかな依存関係しかロードされないことがあります。動的依存関係の遅延読み込みを参照してください。ロードされた各オブジェクトは再配置されます。この結果、シンボル参照とシンボル定義との間にシンボル結合が確立されます。これらの結合は、監査ライブラリの la_symbind() インタフェースに報告されます。

即時依存関係がすべてロードされ、再配置されると、監査ライブラリの la_preinit() インタフェースが呼び出されます。この時点では、プロセスはまだ構築中です。スレッドの初期化と初期化ルーチンコレクションはまだ保留中です。ただし、このインタフェースには、初期プロセスにオブジェクトを追加する場合に便利な制御ポイントが用意されています。

スレッドの初期化が完了すると、監査ライブラリの la_callinit() インタフェースが呼び出されます。この時点で、ロードされたすべてのオブジェクトは実行する準備ができており、その初期化ルーチンは実行に備えて収集されソートされています。初期設定および終了ルーチンを参照してください。la_callinit() 制御ポイントは、アプリケーションコードの実行への遷移にマークを付けます。

アプリケーションコードを実行した結果、シンボル参照とシンボル定義の間で、関数呼び出し結合が確立されます。これらの結合は、監査ライブラリの la_symbind() または la_pltenter()、あるいはその両方のインタフェースに報告されます。遅延ロードでは、シンボル参照を満たすために追加のオブジェクトをロードすることができ、このことは監査ライブラリの la_objopen() に報告されます。

すべての初期設定コードが実行されると、監査ライブラリの la_callentry() インタフェースが呼び出されます。la_callentry() 制御ポイントは、プロセス初期設定の最後と、アプリケーションエントリポイント (通常は start() または main()) への遷移にマークを付けます。

プロセスは実行し続けるので、さらにシンボル結合が発生し、la_symbind() または la_pltenter()、あるいはその両方が呼び出されることがあります。追加の依存関係がロードされ、la_objopen() が呼び出されることもあります。新しい依存関係がアンロードされ、la_objclose() が呼び出されることもあります。オブジェクトのロードまたはアンロードはすべて、la_activity() 呼び出しのペアによって結合されます。最初の la_activity() は、対象の動作 (オブジェクトの追加または削除) を示唆します。2 番目の la_activity() は、プロセス内の依存関係構造が整合していることを示します。監査者は、一貫した通知に従うようにプロセスの検査を制限する必要があります。

監査インタフェースの例

次の単純な例では、動的実行可能プログラム 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: /lib/libc.so.1 loaded
file: /lib/libm.so.2 loaded
file: /usr/lib/locale/en_US/en_US.so.2 loaded
Thur Aug  10 17:03:55 PST 2012

監査インタフェースのデモンストレーション

rtld-監査 インタフェースを使用するデモアプリケーションは、/usr/demo/link_audit の下の pkg:/solaris/source/demo/system パッケージにあります。

sotruss

このデモアプリケーションは、指定アプリケーションの動的オブジェクト間でのプロシージャー呼び出しを追跡します。

whocalls

このデモアプリケーションは、指定アプリケーションに呼び出されるたびに、指定関数のスタック追跡を行います。

perfcnt

このデモアプリケーションは、指定アプリケーションの各関数で費やされた時間を追跡します。

symbindrep

このデモアプリケーションは、指定アプリケーションを読み込むために実行されたすべてのシンボル結合を報告します。

sotruss(1) および whocalls(1)pkg:/developer/linker パッケージに含まれています。perfcntsymbindrep はサンプルプログラムです。これらのアプリケーションは、実際の環境での使用を目的としていません。

監査インタフェースの制限

「rtld-監査」 実装には制限があります。監査ライブラリを設計するときは、これらの制限をよく理解するようにしてください。

アプリケーションコードの実行

オブジェクトがプロセスに追加されると、監査ライブラリは情報を受け取ります。監査ライブラリがこのような情報を受け取るときに、モニターするオブジェクトが実行できる状態でない場合があります。たとえば、監査プログラムは、読み込むオブジェクトのための la_objopen() 呼び出しを受け取ることができます。ただし、そのオブジェクト内のコードを実行するには、その前にオブジェクトの依存関係を読み込んで再配置する必要があります。監査ライブラリは、dlopen(3C) を使用してハンドルを取得して、ロードしたオブジェクトを検査しなければならない場合があります。このハンドルは、dlsym(3C) を使用してインタフェースを検索するために使用できます。ただし、この方法で取得したインタフェースは、そのオブジェクトの初期化が完了したことがわかるまで、呼び出さないようにしてください。

la_pltexit() の使用

la_pltexit() 系列の使用にはいくつかの制限があります。これらの制限は、呼び出し側と「呼び出し先」の間で余分なスタックフレームを挿入して、la_pltexit() 戻り値を提供するための必要から生じたものです。la_pltenter() ルーチンだけを呼び出す場合、この要件は問題になりません。この場合、目的の関数に制御を渡す前に、余分なスタックを整理できます。

これらの制限が原因で、la_pltexit() は、実験的インタフェースとみなされます。問題がある場合には、la_pltexit() ルーチンの使用は避けてください。

スタックを直接検査する関数

スタックを直接検査するか、またはその状態について仮定をたてる少数の関数があります。これらの関数の例としては、setjmp(3C) ファミリ、vfork(2)、および構造へのポインタではなく構造を返す関数があります。これらの関数は、la_pltexit() をサポートするために作成される余分なスタックによって調整されます。

実行時リンカーは、このタイプの関数を検出できないため、監査ライブラリの作成元が、このようなルーチンの la_pltexit() を無効にする必要があります。