ナビゲーションリンクをスキップ | |
印刷ビューの終了 | |
リンカーとライブラリ Oracle Solaris 11 Information Library (日本語) |
スタブオブジェクトは共有オブジェクトで、mapfiles からすべて作成されます。また、コードやデータを持ちませんが、本物のオブジェクト同じリンクインタフェースを提供します。スタブオブジェクトは、実行時には使用できません。しかし、スタブオブジェクトに対してアプリケーションを作成できます。スタブオブジェクトによって、実行時に使用される実際のオブジェクト名が提供されます。
スタブオブジェクトの作成時には、リンカーはコマンド行で指定されているオブジェクトとライブラリファイルをすべて無視するため、スタブを作成するためにこれらのファイルが存在している必要はありません。コンパイル手順が省略でき、リンカーの作業が比較的少ないため、スタブオブジェクトはすぐに作成できます。
スタブオブジェクトは、構築時のさまざまな問題点の解決に利用できます。
速度
最新のマシンで、処理を並列化できるバージョンの make ユーティリティーを使用すると、多数のオブジェクトを同時にコンパイルしてリンクできるため、大幅なスピードアップが図れます。しかし、あるオブジェクトがほかのオブジェクトに依存したり、ほかのほぼすべてのオブジェクトが依存する中核となるオブジェクトセットが存在したりするのが一般的です。すべてのオブジェクトが別のオブジェクトで使用される前に作成されるように、構築の順番を付ける必要があります。この順序付けによってボトルネックが生じ、並列処理できる分量が減ったり、コードを作成できる全体の速度が制限されたりします。
複雑さと正確さ
大規模なコードの場合、さまざまなオブジェクト間に大量の依存関係が存在するときがあります。これらのオブジェクトに関する makefiles などの記述内容が非常に複雑になり、把握と維持が困難になる場合があります。システムの変化に伴って、依存関係が変わる場合があります。これにより、ある makefiles のセットが徐々に不正確になり、競合状態が発生したり、不可解でまれに起こる構築障害に繋がったりします。
依存関係のサイクル
協力し合う共有オブジェクトを作成し、各オブジェクトがリソースを提供して互いに利用し合うようにコードを設計することが望ましい場合もあります。ただし、あるオブジェクトが、そのオブジェクトを使用するオブジェクトの前に作成されなければならない環境では、このようなサイクルはサポートできません。実行時リンカーがこのようなオブジェクト (作成できるとしても) の読み込みと使用に完全に対応していますが、サポートできません。
スタブの共有オブジェクトは、上記の問題点を回避する別のコード作成方法を提供します。ビルドによって作成されるすべての共有オブジェクトに対して、スタブオブジェクトをすぐに作成できます。その後、実際の共有オブジェクトと実行可能ファイルはすべて、リンク時に実際のオブジェクトの代わりを務めるスタブオブジェクトを使用して、平行してどのような順序でも作成できます。実行可能ファイルと実際の共有オブジェクトは保持され、スタブの共有オブジェクトは破棄されます。
スタブオブジェクトは 1 つまたは複数の mapfile から作成され、次の要件を集合的に満たす必要があります。
少なくとも、1 つの mapfile は STUB_OBJECT 指令を指定します。「STUB_OBJECT 指令」を参照してください。
オブジェクトへの外部インタフェースを構成する関数とデータのすべてのシンボルは、mapfile 内に明示的に列挙される必要があります。
mapfile はシンボル範囲の縮小 ('*') を使用して、明示的に列挙されていないすべてのシンボルを外部インタフェースから削除する必要があります。「SYMBOL_SCOPE/SYMBOL_VERSION 指令」を参照してください。
オブジェクトからエクスポートされるすべての大域データには、シンボルのタイプとサイズを指定するため、mapfile に ASSERT シンボル属性が必要です。同じデータを参照するシンボルが複数ある場合、いずれかのシンボルの ASSERT で TYPE と SIZE の属性を指定し、その他では ALIAS 属性を使用して、このプライマリシンボルを参照する必要があります。「ASSERT 属性」を参照してください。
このような mapfile を使用すると、共有オブジェクトのスタブと実オブジェクトそれぞれの作成時に同じコマンド行を使用できます。-z stub オプションは、スタブオブジェクトのリンク編集に追加され、実オブジェクトのリンク編集から削除されます。
これらの考えを示すため、次のコードで名前が idx5 の共有オブジェクトを実装します。このオブジェクトは 5 つの要素を持つ整数の配列からデータをエクスポートします。各要素は初期化され、ゼロから始まる配列インデックスを格納します。このデータは、大域配列として、また結合が弱い代替の別名データシンボルとして、関数インタフェースを介して公開されます。
$ cat idx5.c int _idx5[5] = { 0, 1, 2, 3, 4 }; #pragma weak idx5 = _idx5 int idx5_func(int index) { if ((index < 0) || (index > 4)) return (-1); return (_idx5[index]); }
mapfile では、この共有オブジェクトで提供されるインタフェースを記述する必要があります。
$ cat mapfile $mapfile_version 2 STUB_OBJECT; SYMBOL_SCOPE { _idx5 { ASSERT { TYPE=data; SIZE=4[5] }; }; idx5 { ASSERT { BINDING=weak; ALIAS=_idx5 }; }; idx5_func; local: *; };
次のメインプログラムは、idx5 共有オブジェクトから利用可能なすべてのインデックス値を出力するために使用されます。
$ cat main.c #include <stdio.h> extern int _idx5[5], idx5[5], idx5_func(int); int main(int argc, char **argv) { int i; for (i = 0; i < 5; i++) (void) printf("[%d] %d %d %d\n", i, _idx5[i], idx5[i], idx5_func(i)); return (0); }
次のコマンドでは、この共有オブジェクトのスタブ版を、stublib という名前のサブディレクトリに作成します。elfdump コマンドは、作成されるオブジェクトがスタブであることを検証するために使用されます。スタブの作成に使用されるコマンドは、-z stub オプションを追加する点、および異なる出力ファイル名を使用する点だけが、実オブジェクトのコマンドと異なります。これは、スタブ作成が既存コードに容易に追加できることを示します。
$ cc -Kpic -G -M mapfile -h libidx5.so.1 idx5.c -o stublib/libidx5.so.1 -zstub $ ln -s libidx5.so.1 stublib/libidx5.so $ elfdump -d stublib/libidx5.so | grep STUB [11] FLAGS_1 0x4000000 [ STUB ]
これで、実際の共有オブジェクトの代わりになるスタブオブジェクトを使用して、メインプログラムを構築できるようになりました。スタブオブジェクトは、実行時に実オブジェクトを検索する「実行パス」を設定します。しかし、実オブジェクトはまだ作成されていないため、このプログラムはまだ動作しません。システムがスタブオブジェクトを読み込もうとする試みは拒否されます。実行時リンカーは、実オブジェクトにある実際のコードとデータがスタブオブジェクトにないために、実行できないことをわかっているためです。
$ cc main.c -L stublib -R '$ORIGIN/lib' -lidx5 -lc $ ./a.out ld.so.1: a.out: fatal: libidx5.so.1: open failed: \ No such file or directory Killed $ LD_PRELOAD=stublib/libidx5.so.1 ./a.out ld.so.1: a.out: fatal: stublib/libidx5.so.1: stub shared object \ cannot be used at runtime Killed
実オブジェクトの作成には、スタブオブジェクトを作成するために使用したコマンドと同じものを使用します。-z stub オプションは省かれ、実際の出力ファイルのパスが指定されます。
$ cc -Kpic -G -M mapfile -h libidx5.so.1 idx5.c -o lib/libidx5.so.1
実オブジェクトが lib サブディレクトリに作成されたあとは、このプログラムを実行できます。
$ ./a.out [0] 0 0 0 [1] 1 1 1 [2] 2 2 2 [3] 3 3 3 [4] 4 4 4