リンカーとライブラリ

追加シンボルの定義

入力ファイルから提供されるシンボルのほかに、追加の大域シンボル参照や大域シンボル定義をリンク編集に対して提供することができます。もっとも簡単な形式で、シンボル参照は、リンカーの -u オプションを使用して作成できます。リンカーの -M オプションと関連 mapfile を使用すると柔軟性が高まります。この mapfile を使用すると、大域シンボル参照およびさまざまな大域シンボル定義を定義できます。

u オプションを使用した追加シンボルの定義

-u オプションを指定すると、リンク編集コマンド行から大域シンボル参照を作成するためのメカニズムが使用できます。このオプションを使用して、リンク編集を完全にアーカイブから実行することができます。このオプションは、複数のアーカイブから抽出するオブジェクトを選択する際の柔軟性も高めます。アーカイブの抽出については、「アーカイブ処理」を参照してください。

たとえば、動的実行可能プログラムを、シンボル foobar への参照を実行する再配置可能オブジェクト main.o から生成するとします。この場合、lib1.a 内に組み込まれた再配置可能オブジェクト foo.o からシンボル定義 foo を入手し、さらに lib2.a 内に組み込まれた再配置可能オブジェクト bar.o からシンボル定義 bar を入手します。

ただし、アーカイブ lib1.aにも、シンボル bar を定義する再配置可能オブジェクトが組み込まれています。この再配置可能オブジェクトは、lib2.a に提供されたものとは機能的に異なると想定します。必要なアーカイブ抽出を指定する場合は、次のようなリンク編集を使用できます。


$ cc -o prog -L. -u foo -l1 main.o -l2

-u オプションは、シンボル foo への参照を生成します。この参照によって、再配置可能オブジェクト foo.o がアーカイブ lib1.a から抽出されます。シンボル bar への最初の参照は lib1.a が処理されてから生じる main.o 内で実行されます。このため、再配置可能オブジェクト bar.o はアーカイブ lib2.a から入手されます。


注 –

この単純な例では、lib1.a からの 再配置可能オブジェクト foo.o は、シンボル bar の直接的または間接的な参照は行いません。lib1.abar を参照する場合、処理中に再配置可能オブジェクト bar.olib1.a から抽出されます。アーカイブを処理するリンカーのマルチパスについては、「アーカイブ処理」を参照してください。


mapfile を使用した追加シンボルの定義

広範囲な大域シンボル定義のセットは、リンカーの -M オプションと、関連する mapfile を使用して入手できます。シンボル定義 mapfile のエントリの構文は次のとおりです。


[ name ] {
      scope:
            symbol [ = [ type ] [ value ] [ size ] [ information ] ];
} [ dependency ];
name

このシンボル定義のセットのラベルが存在する場合は、それによってイメージ内の「バージョン定義」を識別できます。第 5 章アプリケーションバイナリインタフェースとバージョン管理を参照してください。

scope

生成される出力ファイル内のシンボルのバインディングの可視性を示しています。mapfile で定義されたすべてのシンボルは、リンク編集プロセス中に、scope (スコープ) 内で global (大域) として処理されます。これらのシンボルは、入力ファイルのいずれかから入手された、同じ名前のほかの大域シンボルに対して解決されます。次の定義と別名は、作成されるオブジェクト内におけるシンボルの可視性を定義します。

default / global

このスコープの大域シンボルは、すべての外部オブジェクトに対して可視となります。このタイプのシンボルに対するオブジェクト内からの参照は実行時に結合されるため、介入が可能となります。この可視性スコープがデフォルトになりますが、これは、ほかのシンボル可視性テクニックを使って降格または削除することができます。このスコープ定義には、シンボルに STV_DEFAULT 可視性が指定された場合と同じ効果があります。表 7–20 を参照してください。

protected / symbolic

このスコープの大域シンボルは、すべての外部オブジェクトに対して可視となります。これらのシンボルに対するオブジェクト内からの参照はリンク編集時に結合されるため、実行時の介入は防止されます。この可視性スコープは、ほかのシンボル可視性テクニックを使って降格または削除することができます。このスコープ定義には、シンボルに STV_PROTECTED 可視性が指定された場合と同じ効果があります。表 7–20 を参照してください。

オブジェクトが単一のシンボルスコープを使って定義される場合、リンク編集時に、オブジェクト内のすべての再配置がそのオブジェクトに結合されます。この単一スコープでは、予約済みのシンボルさえもシンボルスコープに縮小されます。予約シンボル名のリストについては、「出力ファイルの生成」を参照してください。

hidden / local

このスコープの大域シンボルは、ローカル結合を持つシンボルに縮小されます。このスコープのシンボルは、ほかの外部オブジェクトから見えません。このスコープ定義には、シンボルに STV_HIDDEN 可視性が指定された場合と同じ効果があります。表 7–20 を参照してください。

eliminate

このスコープの大域シンボルは hidden です。これらのシンボルテーブルのエントリは削除されます。リンカーの -z redlocsym オプションを使用して、ローカルシンボルを削除することもできます。


注 –

STV_ シンボル可視性属性は、コンパイラの処理するソースコードに埋め込まれたシンボル宣言に由来します。


symbol

シンボル名。この名前により、修飾属性に応じて、シンボル定義またはシンボル参照が生成されます。修飾属性のないもっとも簡潔な形式で、シンボル参照が作成されます。この参照は、u オプションを使用した追加シンボルの定義」で説明した -u オプションを使用して生成する参照とまったく同じものです。 通常、このシンボル名に修飾属性が付いている場合には、シンボル定義は、関連する属性を使用して生成されます。

local スコープが定義される場合、このシンボル名を特別な「自動縮小 (auto-reduction)」指令「*」として定義できます。可視性が明示的に定義されていないシンボルは、生成される動的オブジェクト内のローカル結合に降格されます。明示的な可視性の定義は、mapfile 定義、再配置可能オブジェクト内にカプセル化された可視性定義のいずれかに起因します。

同様に、eliminate スコープが定義されている場合、シンボル名を特別な「自動削除 (auto-elimination)」指令「*」として定義できます。可視性が明示的に定義されていないシンボルは、生成される動的オブジェクトから削除されます。

type

シンボルのタイプ属性を示します。この属性は、COMMONdatafunction のいずれかです。 COMMON 属性の結果は、一時的シンボル定義になります。data および function 属性の結果は、セクションシンボル定義または絶対的なシンボル定義になります。「シンボルテーブルセクション」を参照してください。

data 属性の結果として、OBJT シンボルが作成されます。size を伴うが value を伴わない data 属性では、このシンボルを ELF セクションに関連付けることでセクションシンボルが作成されます。このセクションは、ゼロで埋められます。

function 属性の結果として、FUNC シンボルが作成されます。size を伴うが value を伴わない function 属性では、このシンボルを ELF セクションに関連付けることにより、セクションシンボルが作成されます。このセクションには、空の関数戻り値 void (*)(void) が割り当てられます。

data または function 属性とともに value が指定されると、絶対値を表す ABS セクションインデックスを伴う適切なシンボルタイプが生成されます。

セクションデータシンボルの作成は、フィルタの作成時に役立ちます。実行可能ファイルからフィルタのセクションデータシンボルへの外部参照により、生成中のコピーが適切に再配置されます。「コピー再配置」を参照してください。

value

値の属性を示します。この属性は、V数字の形式をとります。この属性により、シンボル定義が作成されます。

size

サイズの属性を示します。この属性は、S数字の形式をとります。この属性により、シンボル定義が作成されます。

information

このキーワードは、シンボルに次の追加情報を提供します。

AUXILIARY name

このシンボルが共有オブジェクト name に対する補助フィルタであることを示します。「補助フィルタの生成」を参照してください。

DIRECT

このシンボルを直接結合する必要があることを示します。このキーワードをシンボル定義で使用すると、参照が、構築中のオブジェクト内から定義に直接結合されます。このキーワードをシンボル参照で使用すると、定義を提供する依存関係に直接結合されます。「直接結合」を参照してください。このキーワードを PARENT キーワードとともに使用して、実行時に任意の親への直接結合を確立することもできます。

EXTERN

シンボルが、作成されるオブジェクトの外部で定義されていることを示します。通常、このキーワードは、コールバックルーチンへのラベル付けで定義されます。このキーワードを使用して、-z defs オプションで示される未定義シンボルを抑制できます。

このキーワードは、シンボル参照を生成する場合にのみ有効です。このシンボルの定義が、リンク編集時に結合されるオブジェクト内部で生成された場合には、暗黙的に無視されます。

FILTER name

このシンボルが共有オブジェクト name のフィルタであることを示します。「標準フィルタの生成」を参照してください。フィルタシンボルは、入力再配置可能オブジェクトから提供される補助実装を必要としません。したがって、シンボルの種類を定義してこの指令を使用し、絶対シンボルテーブルエントリを作成します。

NODIRECT

このシンボルを直接結合してはならないことを示します。この状態は、作成されるオブジェクト内からの参照と外部参照に適用されます。「直接結合」を参照してください。このキーワードを PARENT キーワードとともに使用して、実行時に任意の親への直接結合を回避することもできます。

PARENT

シンボルが作成中のオブジェクトの親で定義されることを示します。親とは、実行時にこのオブジェクトを明示的な依存関係として参照するオブジェクトです。親は、dlopen(3C) を使用して、このオブジェクトを実行時に参照することもできます。通常、このキーワードは、コールバックルーチンへのラベル付けで定義されます。このキーワードを DIRECT または NODIRECT キーワードとともに使用して、親への直接的または間接的な参照を個別に確立することもできます。このキーワードを使用して、-z defs オプションで示される未定義シンボルを抑制できます。

このキーワードは、シンボル参照を生成する場合にのみ有効です。このシンボルの定義が、リンク編集時に結合されるオブジェクト内部で生成された場合には、暗黙的に無視されます。

dependency

この定義が継承するバージョン定義を示します。第 5 章アプリケーションバイナリインタフェースとバージョン管理を参照してください。

バージョン定義または自動縮小のいずれかの指令が指定されている場合、バージョン情報が作成されるイメージ内に記録されます。このイメージが実行可能プログラムまたは共有オブジェクトである場合には、シンボル縮小も適用されます。

作成されるイメージが再配置可能オブジェクトである場合は、デフォルトにより、シンボル縮小は適用されません。この場合、シンボル縮小はバージョン情報の一部として記録されます。これらの縮小は、再配置可能オブジェクトが最終的に実行可能ファイルまたは共有オブジェクトの生成に使用されるときに適用されます。リンカーの -B reduce オプションを使用すると、再配置可能オブジェクトを生成するときに、強制的にシンボル縮小を実行できます。

バージョン情報の詳細については、第 5 章アプリケーションバイナリインタフェースとバージョン管理に記載されています。


注 –

インタフェース定義を確実に安定させるためには、シンボル名の定義に対しワイルドカードによる拡張を行わないようにします。


次の節では、mapfile 構文を使用した例をいくつか示します。

シンボル参照の定義

次の例では、3 つのシンボル参照を定義する方法を示します。これらの参照を使用して、アーカイブのメンバーを抽出します。このアーカイブ抽出は、複数の -u オプションをリンク編集に指定することにより実現できますが、この例では、最終的なシンボルの範囲を、ローカルに縮小する方法も示しています。


$ cat foo.c
void foo()
{
        (void) printf("foo: called from lib.a\n");
}
$ cat bar.c
void bar()
{
        (void) printf("bar: called from lib.a\n");
}
$ cat main.c
extern  void    foo(), bar();

void main()
{
        foo();
        bar();
}
$ ar -rc lib.a foo.o bar.o main.o
$ cat mapfile
{
        local:
                foo;
                bar;
        global:
                main;
};
$ cc -o prog -M mapfile lib.a
$ prog
foo: called from lib.a
bar: called from lib.a
$ nm -x prog | egrep "main$|foo$|bar$"
[28]    |0x00010604|0x00000024|FUNC |LOCL |0x0  |7      |foo
[30]    |0x00010628|0x00000024|FUNC |LOCL |0x0  |7      |bar
[49]    |0x0001064c|0x00000024|FUNC |GLOB |0x0  |7      |main

大域からローカルへのシンボル範囲の縮小の重要性については、「シンボル範囲の縮小」で説明しています。

絶対シンボルの定義

次の例では、2 つの絶対シンボル定義を定義する方法を示します。そして、これらの定義を使用して、入力ファイル main.c からの参照を解決します。


$ cat main.c
extern  int     foo();
extern  int     bar;

void main()
{
        (void) printf("&foo = %x\n", &foo);
        (void) printf("&bar = %x\n", &bar);
}
$ cat mapfile
{
        global:
                foo = FUNCTION V0x400;
                bar = DATA V0x800;
};
$ cc -o prog -M mapfile main.c
$ prog
&foo = 400
&bar = 800
$ nm -x prog | egrep "foo$|bar$"
[37]    |0x00000800|0x00000000|OBJT |GLOB |0x0  |ABS    |bar
[42]    |0x00000400|0x00000000|FUNC |GLOB |0x0  |ABS    |foo

入力ファイルから入手される場合、関数のシンボル定義またはデータ項目は、通常、データ記憶域の要素に関連しています。mapfile 定義は、このデータ記憶域を構成するためには不十分であるため、これらのシンボルは、絶対値として残しておく必要があります。size が関連付けられるが、value関連付けられない単純な mapfile 定義では、データ記憶域が作成されます。この場合、シンボル定義にはセクションインデックスが伴います。ただし、mapfile 定義に value を関連付けると、絶対シンボルが作成されます。シンボルが共有オブジェクト内で定義される場合、絶対定義は避けるようにしてください。「シンボル定義の増強」を参照してください。

一時的シンボルの定義

mapfileCOMMON または一時的シンボルを定義する場合にも使用できます。ほかのタイプのシンボル定義とは違って、一時的シンボルは、ファイル内の記憶域を占有しませんが、実行時に割り当てる記憶域の定義は行います。そのため、このタイプのシンボル定義は、作成される出力ファイルの記憶域割り当ての一因となります。

一時的シンボルの特徴は、ほかのシンボルタイプとは異なり、その「値」の属性によって、その配列要件が示される点です。そのため、リンク編集の入力ファイルから入手される一時的定義の再配列に mapfile 定義を使用できます。

次の例では、2 つの一時的シンボルの定義を示しています。シンボル foo は、新しい記憶領域を定義しているのに対し、シンボル bar は、実際に、ファイル main.c 内の同じ一時的定義の配列を変更するために使用されます。


$ cat main.c
extern  int     foo;
int             bar[0x10];

void main()
{
        (void) printf("&foo = %x\n", &foo);
        (void) printf("&bar = %x\n", &bar);
}
$ cat mapfile
{
        global:
                foo = COMMON V0x4 S0x200;
                bar = COMMON V0x100 S0x40;
};
$ cc -o prog -M mapfile main.c
ld: warning: symbol `bar' has differing alignments:
        (file mapfile value=0x100; file main.o value=0x4);
        largest value applied
$ prog
&foo = 20940
&bar = 20900
$ nm -x prog | egrep "foo$|bar$"
[37]    |0x00020900|0x00000040|OBJT |GLOB |0x0  |16     |bar
[42]    |0x00020940|0x00000200|OBJT |GLOB |0x0  |16     |foo

注 –

このシンボル解決の診断は、リンカーの -t オプションを使用すると表示されません。


シンボル定義の増強

共有オブジェクト内での絶対データシンボルの作成は避けるべきです。通常、動的実行可能ファイルから、共有オブジェクト内のデータ項目への外部参照には、コピー再配置の作成が必要になります。「コピー再配置」を参照してください。このような再配置を行う場合は、データ項目をデータ記憶領域と関連付けるべきです。この関連付けは、再配置可能なオブジェクトファイル内にシンボルを定義することで行うことができます。この関連付けは、mapfile 内でシンボルを size 宣言あり、 value 宣言なしで定義しても行うことができます。mapfile を使用した追加シンボルの定義」を参照してください。

データシンボルにはフィルタを適用できます。「フィルタとしての共有オブジェクト」を参照してください。このようなフィルタ適用を行うため、オブジェクトファイル定義は mapfile 定義で増強できます。次の例では、関数定義とデータ定義を含むフィルタを作成します。


$ cat mapfile
{
        global:
                foo = FUNCTION FILTER filtee.so.1;
                bar = DATA S0x4 FILTER filtee.so.1;
        local:
                *;
};
$ cc -o filter.so.1 -G -Kpic -h filter.so.1 -M mapfile -R.
$ nm -x filter.so.1 | egrep "foo|bar"
[39]    |0x000102b0|0x00000004|OBJT |GLOB |0    |12     |bar
[45]    |0x00000000|0x00000000|FUNC |GLOB |0    |ABS    |foo
$ elfdump -y filter.so.1 | egrep "foo|bar"
       [1]  F        [0] filtee.so.1        bar
       [7]  F        [0] filtee.so.1        foo

実行時に、外部オブジェクトからこれらのシンボルのいずれかへの参照は、「フィルティー」内の定義に解決されます。