リンカーとライブラリ

単純な解決

単純なシンボル解決は、もっとも一般的です。この場合、類似する特徴を持ち、どちらかが優先される 2 つのシンボルが検出されます。このシンボル解決は、リンカーによって自動的に実行されます。たとえば、同じ結合を持つシンボルがあり、1 つのファイルからのシンボル参照が、別のファイルの定義または一時的シンボル定義に結合されているとします。あるいは、あるファイルからの一時的シンボル定義は、ほかのファイルからの定義シンボルの定義に結合されます。この解決は、2 つの再配置可能オブジェクト間、および再配置可能オブジェクトと共有オブジェクト依存関係内で検出された最初の定義の間で発生する場合があります。

解決されるシンボルは、大域結合またはウィーク結合されます。再配置可能オブジェクト内では、ウィーク結合の方が、大域結合よりも優先度が低くなります。異なる結合を伴う再配置可能オブジェクトシンボルは、わずかに変更された基本規則に従って解決されます。

ウィークシンボルは通常、個別にあるいは大域シンボルの別名として、コンパイラによって定義されます。このメカニズムでは、#pragma 定義を使用します。


$ cat main.c
#pragma weak    bar
#pragma weak    foo = _foo

int             bar = 1;

int _foo()
{
        return (bar);
}
$ cc -o main.o -c main.c
$ nm -x main.o
[Index]   Value      Size      Type  Bind  Other Shndx   Name
...............
[7]     |0x00000000|0x00000004|OBJT |WEAK |0x0  |3      |bar
[8]     |0x00000000|0x00000028|FUNC |WEAK |0x0  |2      |foo
[9]     |0x00000000|0x00000028|FUNC |GLOB |0x0  |2      |_foo

ウィークの別名 foo に、大域シンボル _foo と同じ属性が割り当てられていることに注意してください。この関係は、リンカーによって保持され、その結果、シンボルには出力イメージ内の同じ値が割り当てられます。シンボル解決においては、ウィーク定義シンボルは、同じ名前の大域定義によって自動的に上書きされます。

単純なシンボル解決のもう 1 つの形式である「割り込み」は、再配置可能オブジェクトと共有オブジェクト間、または複数の共有オブジェクト間で発生します。この場合、シンボルが複数回定義されていれば、再配置可能オブジェクト、または複数の共有オブジェクト間の最初の定義がリンカーによって暗黙のうちに採用されます。再配置可能オブジェクトの定義、または最初の共有オブジェクトの定義は、ほかのすべての定義上に割り込みを行うといわれます。この割り込みを使用して、別の共有オブジェクトが提供する機能を無効にすることができます。再配置可能オブジェクトと共有オブジェクトの間、または複数の共有オブジェクト間で発生する複数回定義されたシンボルは、同一に扱われます。シンボルのウィーク結合や大域結合は、これとは無関係です。最初の定義を解決することにより、シンボルの結合に関係なく、リンカーと実行時リンカーの両方が一貫して動作します。

共有オブジェクト内部で定義されたウィークシンボルを、同じ共有オブジェクトに対するシンボル割り込みと組み合わせることにより、有用なプログラミングテクニックを使用できます。たとえば、標準 C ライブラリは、再定義可能ないくつかのサービスを提供していますが、ANSI C は、システムに必要な一連の標準サービスを定義します。厳格な適合プログラムでは、これらのサービスを置き換えることはできません。

たとえば、関数 fread(3C) は、ANSI C ライブラリの関数です。システム関数 read(2) は、ANSI C ライブラリの関数ではありません。適合する ANSI C プログラムは、read(2) を再定義でき、予測できる方法で fread(3C) を使用できなければなりません。

ここでの問題は、read(2) が、標準 C ライブラリ内に fread(3C) を実装する基盤になることです。このため、read(2) を再定義するプログラムは、fread(3C) の実装を混乱させる可能性があります。この混乱を避けるために、ANSI C では、実装に予約されていない名前を実装に使用できないように定めています。次の #pragma 指令で、このような予約名だけを定義します。この名前を使用して、関数 read(2) の別名を生成します。


#pragma weak read = _read

こうすることにより、ユーザーは、_read() 関数を使用している fread(3C) の実装を危険にさらすことなく、自分専用の read() 関数を自由に定義できます。

このリンカーでは、標準 C ライブラリの共有オブジェクトまたはアーカイブバージョンのどちらかにリンクしている場合でも、read() を再定義できます。前者の場合には、割り込みによって方法が決められます。後者の場合には、read(2) の C ライブラリの定義をウィークにすることにより、自動的に上書き可能になります。

リンカーの -m オプションを使用して、割り込みされるすべてのシンボル参照のリストを、セクションの読み込みアドレス情報とともに標準出力に書き込んでください。