リンカーとライブラリ

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

共有オブジェクトは、「フィルタ」として機能するように定義できます。この手法には、フィルタが提供するインタフェースと、代替共有オブジェクトとの関連付けが含まれます。代替共有オブジェクトは実行時に、「フィルタ」により提供される 1 つまたは複数のインタフェースを供給します。この代替共有オブジェクトは 「フィルティー」と呼ばれます。「フィルティー」は、共有オブジェクトと同じように構築されます。

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

mapfile のキーワードである FILTER または AUXILIARY を使用することで、共有オブジェクト内に定義された個別インタフェースをフィルタとして定義できます。また、特定の共有オブジェクトが提供するすべてのインタフェースをフィルタとして定義することもできます。それには、リンカーの -F または -f フラグを使用します。これらの手法は、一般に個別に使用されますが、同じ共有オブジェクトの中で組み合わせることもできます。

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

標準フィルタ処理

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

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

実行時に処理できない「フィルティー」はスキップされます。「フィルティー」内に標準フィルタシンボルが見つからない場合も、「フィルティー」はスキップされます。どちらの場合も、フィルタにより提供されるシンボル定義は、このシンボル検索を満たすためには使用されません。

補助フィルタ処理

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

リンカーの mapfile キーワード AUXILIARY またはリンカーの -f フラグを使用すると、インタフェースは補助フィルタとして機能するように定義されます。この mapfile キーワードまたはフラグは、実行時にシンボル定義を提供できる 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

標準フィルタ処理は、2 つの方法のいずれかで実行できます。共有オブジェクトによって提供されるすべてのインタフェースをフィルタとして宣言するには、リンカーの -F フラグを使用します。フィルタとなる共有オブジェクトの個々のインタフェースを宣言するには、リンカーの mapfileFILTER キーワードを使用します。

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


$ cat filter.c
char * bar = NULL;

char * foo()
{
	return (NULL);
}
$ 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

動的実行可能ファイルまたは共有オブジェクトを作成する場合、リンカーは標準フィルタ filter.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();

void 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

次の例では、共有オブジェクト filter.so.2 は、インタフェースの 1 つである foo を フィルティー filtee.so.1 上のフィルタとして定義します。


注 –

foo() にはソースコードが提供されていないので、mapfile のキーワードである FUNCTION を使用して、foo のシンボルテーブルエントリが作成されることを確認します。



$ cat filter.c
char * bar = "defined in filter";
$ cat mapfile
{
	global:
		foo = FUNCTION FILTER filtee.so.1;
};
$ cc -o filter.so.2 -G -K pic -h filter.so.2 -M mapfile -R. filter.c
$ elfdump -d filter.so.2 | egrep "SONAME|FILTER"
    [2]  SONAME           0xd8     filter.so.2
    [3]  SUNW_FILTER      0xfb     filtee.so.1
$ elfdump -y filter.so.2 | egrep "foo|bar"
    [1]  F    [3] filtee.so.1      foo
   [10]  D        <self>           bar

実行時にフィルタのシンボル foo を参照すると、必ず「フィルティー」filtee.so.1 がさらに読み込まれます。実行時リンカーは、「フィルティー」を使用して、filter.so.2 が定義したシンボル foo だけを解決します。シンボル bar への参照は、 filter.so.2 からのシンボルを常に使用し、このシンボルに対して「フィルティー」処理は定義されません。

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


$ cc -o prog main.c -R. filter.so.2
$ prog
foo is defined in filtee: bar is defined in filter

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

標準フィルタは、既存の共有オブジェクトのサブセットインタフェースを定義するための便利なメカニズムを提供します。標準フィルタは、多数の既存の共有オブジェクトに及ぶインタフェースグループを作成します。標準フィルタはまた、インタフェースをその実装にリダイレクトする手段も提供します。一部の標準フィルタは、Solaris OS で使用されます。

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

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

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

libnsl.so.1 は、標準フィルタ gethostname(3C)libc.so.1 に対して定義します。以前は、libnsl.so.1libc.so.1 もこのシンボルの同じ実装を提供していました。libnsl.so.1 をフィルタとして設定することで、gethostname() の実装は 1 つだけ必要となります。libnsl.so.1 は継続して gethostname() をエクスポートするため、このライブラリインタフェースも以前のリリースと互換性があります。

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

フィルタ内にデータシンボルを生成するときは、常にデータをセクションに関連付けてください。この関連付けは、再配置可能なオブジェクトファイル内にシンボルを定義することで行うことができます。この関連付けは、mapfile 内でシンボルを size 宣言あり、 value 宣言なしで定義しても行うことができます。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

補助フィルタ処理は、2 つの方法のいずれかで提供できます。共有オブジェクトによって提供されるすべてのインタフェースを補助フィルタとして宣言するには、リンカーの -f フラグを使用します。共有オブジェクトの個々のインタフェースを補助フィルタとして宣言するには、リンカーの mapfileAUXILIARY キーワードを使用します。

次の例では、共有オブジェクト filter.so.1 が補助フィルタとして定義されています。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();

void 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

次の例では、共有オブジェクト filter.so.2 は、インタフェース foo をフィルティーfiltee.so.1 上の補助フィルタとして定義します。


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

char * foo()
{
        return ("defined in filter");
}
$ cat mapfile
{
	global:
		foo = AUXILIARY filtee.so.1;
};
$ cc -o filter.so.2 -G -K pic -h filter.so.2 -M mapfile -R. filter.c
$ elfdump -d filter.so.2 | egrep "SONAME|AUXILIARY"
    [2]  SONAME           0xd8     filter.so.2
    [3]  SUNW_AUXILIARY   0xfb     filtee.so.1
$ elfdump -y filter.so.2 | egrep "foo|bar"
    [1]  A    [3] filtee.so.1      foo
   [10]  D        <self>           bar

実行時にフィルタのシンボル foo を参照すると、必ず「フィルティー」filtee.so.1 が検索されます。「フィルティー」が見つかると、「フィルティー」が読み込まれます。「フィルティー」は filter.so.2 によって定義されたシンボル foo の解決に使用されます。「フィルティー」が検索されなかった場合、filter.so.2 によって定義されたシンボル foo が使用されます。シンボル bar への参照は、 filter.so.2 からのシンボルを常に使用し、このシンボルに対して「フィルティー」処理は定義されません。

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


$ cc -o prog main.c -R. filter.so.2
$ prog
foo is defined in filtee: bar is defined in filter

「フィルティー」 filtee.so.1が存在しない場合、prog を実行すると、foobar がフィルタ filter.so.2 から取得されます。


$ prog
foo is defined in filter: bar is defined in filter

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

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


注 –

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


フィルタ処理の組み合わせ

標準フィルタを定義する個別インタフェースと補助インタフェースを定義する個別インタフェースを、同一の共有オブジェクト内に定義することができます。こうしたフィルタ定義の組み合わせを実現するには、mapfile のキーワードである FILTERAUXILIARY を使って、必要な「フィルティー」を割り当てます。

-F または -f オプションを使用して自身のインタフェースのすべてをフィルタとして定義する共有オブジェクトは、標準フィルタか補助フィルタのどちらかです。

共有オブジェクトでは、個々のインタフェースをフィルタとして機能するように定義するとともに、そのオブジェクトのすべてのインタフェースをフィルタとして機能するように定義することができます。その場合、特定のインタフェースに対して定義された個別フィルタ処理が、まず処理されます。個別インタフェースフィルタに対する「フィルティー」を確立できなかった場合は、フィルタのすべてのインタフェースに対して定義された「フィルティー」が必要に応じてフォールバックを提供します。

たとえば、フィルタ filter.so.1 があるとします。このフィルタでは、すべてのインタフェースが「フィルティー」filtee.so.1 に対する補助フィルタとして機能するように、リンカーの -f フラグを使って定義されています。さらに filter.so.1 では、個別インタフェース foo が「フィルティー」foo.so.1 に対する標準フィルタとなるように、mapfile のキーワード FILTER を使って定義されています。さらに filter.so.1 では、個別インタフェース bar が「フィルティー」bar.so.1 に対する補助フィルタとなるように、mapfile のキーワード AUXILIARY を使って定義されています。

foo への外部参照が発生すると、「フィルティー」foo.so.1 が処理されます。foofoo.so.1 で見つからなかった場合、このフィルタに対する処理はそれ以上実行されません。この場合にフォールバック処理が実行されない理由は、foo が標準フィルタとして定義されているからです。

bar への外部参照が発生すると、「フィルティー」bar.so.1 が処理されます。barbar.so.1 で見つからなかった場合、「フィルティー」filtee.so.1 によるフォールバック処理が実行されます。この場合にフォールバック処理が実行される理由は、bar が補助フィルタとして定義されているからです。barfiltee.so.1 で見つからなかった場合、最終的にはフィルタ filter.so.1 内の bar の定義に基づいて外部参照が解決されます。

「フィルティー」の処理

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

実行時に-「フィルティー」の即時処理を起動するフィルタを作成する場合には、リンカーの z loadfltr オプションを使用できます。さらに、LD_LOADFLTR 環境変数を任意の値に設定することで、プロセス内のすべての「フィルティー」の即時処理を開始できます。