リンカーとライブラリ

フィルタとしての共有オブジェクト

共有オブジェクトは、 それらが提供するインタフェースの「フィルタ」として機能するように定義できます。この手法では、代替共有オブジェクトへのフィルタインタフェースの関連付けが行われます。代替共有オブジェクトは実行時にシンボル定義を供給するもので、「フィルティー (フィルタ対象)」として参照されます。「フィルティー」は、共有オブジェクトと同じように構築されます。

フィルタ処理は、実行時環境からコンパイル環境を抽象化するメカニズムを提供します。リンク編集時には、フィルタインタフェースに結合するシンボル参照は、フィルタシンボル定義に解決されます。実行時には、フィルタインタフェースに結合するシンボル参照は代替共有オブジェクトにリダイレクトできます。共有オブジェクトは、それ自体がフィルタとして提供するすべてのインタフェースを定義できます。

フィルタ処理には、次に示す 2 つの形があります。

標準フィルタ処理

このフィルタ処理で必要となるのは、フィルタ処理対象のインタフェースのシンボルテーブルエントリだけです。実行時には、「フィルティー」からフィルタシンボル定義の実装を提供する必要があります。

リンカーの -F フラグを使用すると、インタフェースは標準フィルタとして機能するように定義されます。このフラグは、実行時にシンボル定義を供給する 1 つ以上の「フィルティー」の名前を必要とします。

実行時に「フィルティー」の処理ができない場合や、「フィルティー」内にフィルタシンボルが見つからない場合は、このシンボル検索では「フィルティー」はスキップされます。

補助フィルタ処理

このフィルタ処理は標準フィルタ処理と類似したメカニズムを提供しますが、補助フィルタインタフェースに対応するフォールバック実装がフィルタに含まれます。実行時には、「フィルティー」からシンボル定義の実装を提供できます。

リンカーの -f フラグを使用すると、インタフェースは補助フィルタとして機能するように定義されます。このフラグは、実行時にシンボル定義を供給する可能性がある 1 つ以上の「フィルティー」の名前を必要とします。

「フィルティー」 を実行時に処理できないか、あるいは「フィルティー」内に補助フィルタシンボルが見つからないと、フィルタ内のシンボルの実装が使用されます。

標準フィルタの生成

標準フィルタを生成するには、まずフィルタ処理を適用する「フィルティー」を定義する必要があります。次の例では、シンボル foobar を提供する「フィルティー」filtee.so.1 を構築します。


$ cat filtee.c
char * bar = "defined in filtee";

char * foo()
{
        return("defined in filtee");
}
$ cc -o filtee.so.1 -G -K pic filtee.c

共有オブジェクトによって提供されるすべてのインタフェースをフィルタとして宣言するには、リンカーの -F フラグを使用してその共有オブジェクトをフィルタとして定義します。

次の例では、共有オブジェクト filter.so.1 がフィルタとして定義されています。この共有オブジェクトは、シンボル foobar を提供し、それ自体が「フィルティー」filtee.so.1 のフィルタであることを示しています。この例では、コンパイラドライバが -F オプションを解釈しないように、環境変数 LD_OPTIONS が使用されています。


$ cat filter.c
char * bar = 0;

char * foo()
{
	return (0);
}
$ LD_OPTIONS='-F filtee.so.1' \
cc -o filter.so.1 -G -K pic -h filter.so.1 -R. filter.c
$ elfdump -d filter.so.1 | egrep "SONAME|FILTER"
    [2]  SONAME           0xee     filter.so.1
    [3]  FILTER           0xfb     filtee.so.1

動的実行可能ファイルまたは共有オブジェクトを作成するために標準フィルタ filtee.so.1 を参照する場合、リンカーはフィルタのシンボルテーブルの情報を使用してシンボル解決を行います。しかし、実行時にフィルタのシンボルを参照すると、必ず「フィルティー」filtee.so.1 がさらに読み込まれます。実行時リンカーはこの「フィルティー」を使用して、filter.so.1 によって定義されたシンボルを解決します。この「フィルティー」 が見つからないか、あるいは「フィルティー」内にフィルタシンボルが見つからない場合は、このシンボル検索でそのフィルタはスキップされます。

たとえば、次の動的実行可能ファイル prog は、シンボル foobar を参照します。これらのシンボルは、フィルタ filter.so.1 からのリンク編集中に解決されます。prog を実行すると、foobar が、フィルタ filter.so.1 からではなく、「フィルティー」filtee.so.1 から取得されます。


$ cat main.c
extern char * bar, * foo();

main()
{
        (void) printf("foo is %s: bar is %s\n", foo(), bar);
}
$ cc -o prog main.c -R. filter.so.1
$ prog
foo is defined in filtee: bar is defined in filtee

これらの例では、「フィルティー」filtee.so.1 がフィルタに一意に関連付けられています。このため、prog を実行した結果読み込まれる可能性があるほかのオブジェクトからのシンボル検索を満たすために使用することができません。

標準フィルタは、既存の共有オブジェクトのサブセットインタフェース、または多数の既存の共有オブジェクトに及ぶインタフェースグループを定義するためのメカニズムを提供します。標準フィルタはまた、インタフェースをその実装にリダイレクトする手段も提供します。Solaris オペレーティング環境では、いくつかの標準フィルタが使用されています。

/usr/lib/libsys.so.1 フィルタは、標準 C ライブラリ /usr/lib/libc.so.1 のサブセットを提供します。 このサブセットは、ABI に準拠するアプリケーションがインポートしなければならない C ライブラリ内の ABI に準拠する関数とデータ項目を表わします。

/usr/lib/libxnet.so.1 フィルタは、複数の「フィルティー」を使用します。このライブラリは、/usr/lib/libsocket.so.1/usr/lib/libnsl.so.1、および /usr/lib/libc.so.1 から、ソケットと XTI インタフェースを提供します。

/usr/lib/libdl.so.1 フィルタは、実行時リンカーへのユーザーインタフェースを定義します。このインタフェースは、コンパイル環境で (libdl.so.1 から) 参照されるシンボルと、実行時環境内で (ld.so.1(1) から) 作成される実際の実装結合間の抽象化を提供します。

標準フィルタ内のコードは実行時に参照されることはないため、フィルタとして定義された関数に内容を追加しても意味がありません。どのようなフィルタコードでも再配置を必要とする場合があり、実行時にそのフィルタを処理すると不要なオーバーヘッドが生じます。関数は空のルーチンとして定義するか、直接 mapfile から定義してください。追加シンボルの定義を参照してください。


注 –

リンカーは、処理される最初の再配置可能ファイルの ELF クラスを使用して、作成するオブジェクトのクラスを管理します。64 ビットフィルタを mapfile だけから作成するには、リンカーの -64 オプションを使用します。


フィルタ内にデータシンボルを生成するときは、常にデータ項目を初期化してください。このようにデータを定義することで、動的実行可能ファイルからの参照が正しく確立されます。

リンカーによって実行される、より複雑なシンボル解決の中には、シンボルサイズを含むシンボルの属性に関する知識を必要とするものがあります。このため、フィルタ内のシンボルの属性が「フィルティー」内のシンボルの属性と一致するようにシンボルを生成する必要があります。これにより、リンク編集処理では、実行時に使用されるシンボル定義と互換性のある方法でフィルタが解析されます。詳細は、シンボル解決を参照してください。

補助フィルタの生成

補助フィルタを生成するには、まずフィルタ処理を適用する「フィルティー」を定義する必要があります。次の例では、シンボル foo を提供する「フィルティー」filtee.so.1 を構築します。


$ cat filtee.c
char * foo()
{
        return("defined in filtee");
}
$ cc -o filtee.so.1 -G -K pic filtee.c

共有オブジェクトによって提供されるすべてのインタフェースを補助フィルタとして宣言するには、リンカーの -f フラグを使用してその共有オブジェクトを補助フィルタとして定義します。

次の例では、共有オブジェクト filter.so.1 が補助フィルタとして定義されています。この共有オブジェクトは、シンボル foobar を提供し、それ自体が「フィルティー」filtee.so.1 の補助フィルタであることを示しています。この例では、コンパイラドライバが -f オプションを解釈しないように、環境変数 LD_OPTIONS が使用されています。


$ cat filter.c
char * bar = "defined in filter";

char * foo()
{
        return ("defined in filter");
}
$ LD_OPTIONS='-f filtee.so.1' \
cc -o filter.so.1 -G -K pic -h filter.so.1 -R. filter.c
$ elfdump -d filter.so.1 | egrep "SONAME|AUXILIARY"
    [2]  SONAME           0xee     filter.so.1
    [3]  AUXILIARY        0xfb     filtee.so.1

動的実行可能ファイルまたは共有オブジェクトを作成するために補助フィルタ filter.so.1 を参照する場合、リンカーはフィルタのシンボルテーブルの情報を使用してシンボル解決を行います。しかし、実行時にフィルタのシンボルを参照すると、「フィルティー」filtee.so.1 が検索されます。この「フィルティー」が見つかると、実行時リンカーは、この「フィルティー」を使用して、filter.so.1 によって定義されたすべてのシンボルを解決します。この「フィルティー」が見つからないか、あるいは「フィルティー」内にフィルタからのシンボルが見つからない場合は、フィルタ内の元のシンボルが使用されます。

たとえば、次の動的実行可能プログラム prog は、シンボル foobar を参照します。これらのシンボルは、リンク編集中にフィルタ filter.so.1 から解決されます。prog を実行すると、foo が、フィルタ filter.so.1 からではなく、「フィルティー」filtee.so.1 から取得されます。しかし、bar はフィルタ filter.so.1 から取得されます。これは、「フィルティー」filtee.so.1 内にこのシンボルの代替定義が存在しないためです。


$ cat main.c
extern char * bar, * foo();

main()
{
        (void) printf("foo is %s: bar is %s\n", foo(), bar);
}
$ cc -o prog main.c -R. filter.so.1
$ prog
foo is defined in filtee: bar is defined in filter

これらの例では、「フィルティー」filtee.so.1 がフィルタに一意に関連付けられています。このため、prog を実行した結果読み込まれる可能性があるほかのオブジェクトからのシンボル参照を満たすために使用することができません。

補助フィルタは、既存の共有オブジェクトの代替インタフェースを定義するメカニズムとなります。このメカニズムは Solaris オペレーティング環境で使用されて、プラットフォーム固有の共有オブジェクト内に最適化された機能を提供します。例は、命令セット固有の共有オブジェクトシステム固有の共有オブジェクトを参照してください。


注 –

環境変数 LD_NOAUXFLTR を設定して、実行時リンカーの補助フィルタ処理を無効にすることができます。補助フィルタはプラットフォーム固有の最適化に使用されることが多いので、「フィルティー」の使用およびそれらの性能インパクトを評価する場合にこのオプションが便利です。


「フィルティー」の処理

実行時リンカーによるフィルタ処理は、フィルタ内のシンボルへの参照が生じるまで、「フィルティー」の読み込みを遅延します。この実装は、必要に応じてモード RTLD_LOCAL を使用し各「フィルティー」に対して dlopen(3DL) を実行するフィルタに似ています。この実装は、ldd(1) などのツールによる依存関係の報告における違いの理由となります。

フィルタを作成して、実行時にその「フィルティー」を即時処理する場合は、リンカーの -z loadfltr オプションを使用します。また、環境変数 LD_LOADFLTR に何らかの値を設定することでも、プロセス内の「フィルティー」すべての即時処理を行わせることができます。