リンカーとライブラリ

リンカーのサポートインタフェース

第 2 章「リンカー」で説明したように、リンカーは、ファイルのオープンやこれらのファイルからのセクションの連結を含む多数の操作を実行します。これらの操作の監視、および場合によっては変更は、コンパイルシステムのコンポーネントにとって有益なことがよくあります。

このセクションでは、入力ファイル検査、および場合によってはリンク編集を構成するファイルの入力ファイルデータ変更用にサポートされているインタフェースついて説明します。このインタフェースは、ld-サポートインタフェースと呼ばれます。このインタフェースを使用する 2 つのアプリケーションは、このインタフェースを使用して再配置可能オブジェクト内の情報デバッグを処理するリンカーそのものと、このインタフェースを使用して状態情報を保存する make(1S) ユーティリティです。

ld-サポートインタフェースは、1 つまたは複数のサポートインタフェースルーチンを提供するサポートライブラリから構成されています。このライブラリはリンク編集プロセスの一部として読み込まれ、検出されたサポートルーチンはすべてリンク編集の各段階で呼び出されます。

このインタフェースを使用する場合は、elf(3ELF) 構造とファイル形式に詳しくなければなりません。

サポートインタフェースの呼び出し

リンカーは、SGS_SUPPORT 環境変数またはリンカーの -S オプションのどちらかによって提供される 1 つまたは複数のサポートライブラリを受け入れます。環境変数は、コロンで区切られたサポートライブラリのリストから構成されています。


$ SGS_SUPPORT=./support.so.1:libldstab.so.1 cc ...

-S オプションは、単一のサポートライブラリを指定します。次のように複数の -S オプションを指定できます。


$ LD_OPTIONS="-S./support.so.1 -Slibldstab.so.1" cc ...

サポートライブラリは、共有オブジェクトの 1 つです。リンカーは、各サポートライブラリに対してライブラリが指定された順序で、dlopen(3DL) を実行します。環境変数と -S オプションの両方がある場合は、環境変数によって指定されたサポートライブラリが最初に処理されます。各サポートライブラリは、dlsym(3DL) によって、サポートインタフェースルーチンがないかどうかをさらに検索されます。これらのサポートルーチンは、リンク編集の各段階で呼び出されます。


注 -

デフォルトごとに、Solaris サポートライブラリ libldstab.so.1 は、リンカーを使用して、入力再配置可能オブジェクト内に提供されるコンパイラ生成デバッグ情報を処理、圧縮します。このデフォルト処理は、 -S オプションを使用して指定されたサポートライブラリでリンカーを呼び出すと抑止されます。各サポートライブラリサービスだけでなく libldstab.so.1 のデフォルト処理も必要な場合は、リンカーに提供されたサポートライブラリのリストに libldstab.so.1 を明示的に追加する必要があります。


32 ビットおよび 64 ビット環境

「新しい機能」で説明しているように、64 ビットリンカー (ld(1)) は 32 ビットのオブジェクトを生成でき、32 ビットリンカーは 64 ビットのオブジェクトを生成できます。これらのオブジェクトはそれぞれ、定義されているサポートインタフェースに関連付けられています。

64 ビットオブジェクトのサポートインタフェースは 32 ビットオブジェクトのサポートインタフェースと似ていますが、末尾に「64」という接尾辞が付きます。たとえば、ld_start() および ld_start64() のようになります。この規則により、サポートインタフェースの両方の実装状態を、libldstab.so.1 という単一の共有オブジェクトの 32 ビットと 64 ビットの各クラスに常駐させることができます。

サポートインタフェース関数

ld-サポートインタフェースはすべて、ヘッダファイル link.h に定義されています。インタフェース引数はすべて、基本的な C タイプまたは ELF タイプです。ELF データタイプは、ELF アクセスライブラリ libelf によって調べることができます (libelf の内容については、elf(3ELF) を参照)。次のインタフェース関数は、ld-サポートインタフェースによって提供されており、予定の使用順序に従って記述されています。

ld_start()

void ld_start(const char * name, const Elf32_Half type,
    const char * caller);

void ld_start64(const char * name, const Elf64_Half type,
    const char * caller);

この関数は、リンカーコマンド行の初期妥当性検査の後に呼び出されて、入力ファイル処理の開始を示します。

name は、作成される出力ファイル名を示します。type は出力ファイルタイプであり、ET_DYNET_RELET_EXEC のどれかです (sys/elf.h に定義)。caller は、インタフェースを呼び出すアプリケーションを示します。これは通常、/usr/ccs/bin/ld です。

ld_file()

void ld_file(const char * name, const Elf_Kind kind, int flags,
    Elf * elf);

void ld_file64(const char * name, const Elf_Kind kind, int flags,
    Elf * elf);

この関数は、ファイルデータの処理が実行される前に、各入力ファイルに対して呼び出されます。

name は処理される入力ファイルを示します。kind は入力ファイルのタイプを示し、 ELF_K_AR または ELF_K_ELF のどちらかになります (libelf.h に定義)。flags は、リンカーによるファイルの取得方法を示し、次の 1 つまたは複数の定義にすることができます。

  • LD_SUP_DERIVED - ファイル名はコマンド行に明示的に指定されていない。 -l の拡張から派生するか、または抽出されたアーカイブ構成要素を示す

  • LD_SUP_INHERITED - ファイルは、コマンド行共有オブジェクトの依存関係として取得される

  • LD_SUP_EXTRACTED - ファイルは、アーカイブから抽出される

flags 値が指定されていない場合、入力ファイルはコマンド行に明示的に指定されています。elf は、ファイル ELF 記述子へのポインタです。

ld_section()

void ld_section(const char * name, Elf32_Shdr * shdr,
        Elf32_Word sndx, Elf_Data * data, Elf * elf);
void ld_section64(const char * name, Elf64_Shdr * shdr,
        Elf64_Word sndx, Elf_Data * data, Elf * elf);

この関数は、セクションデータの処理が実行される前に、入力ファイルの各セクションに対して呼び出されます。

name は、入力セクション名を示します。shdr は、関連のセクションヘッダへのポインタを示します。sndx は、入力ファイル内のセクションインデックスです。data は、関連データバッファへのポインタを示します。elf は、ファイル ELF 記述子へのポインタです。

データの変更は、データ自体を再割り当てして、Elf_Data バッファに d_buf ポインタを再割り当てすることによって可能になります。データへの変更はすべて、Elf_Data バッファの d_size 要素を保証しなければなりません。出力イメージの一部になる入力セクションでは、d_size 要素をゼロに設定すると、出力イメージからデータが効率的に削除されます。


注 -

リンカーの -s オプションによって取り除かれるセクションは、ld_section() に報告されません。


ld_atexit()

void ld_atexit(int status);

void ld_atexit64(int status);

この関数は、リンク編集の終了時に呼び出されます。

status は、リンカーによって返される exit(2) コードであり、EXIT_FAILURE または EXIT_SUCCESS のどちらかです (stdlib.h に定義)。

サポートインタフェースの例

次の例では、32 ビットリンク編集の一部として処理される再配置可能オブジェクトファイルのセクション名 (表 7-16 を参照) を出力するサポートライブラリを作成しています。


$ cat support.c
#include        <link.h>
 
static int      indent = 0;
 
void
ld_start(const char * name, const Elf32_Half type,
    const char * caller)
{
        (void) printf("output image: %s¥n", name);
}
 
void
ld_file(const char * name, const Elf_Kind kind, int flags,
    Elf * elf)
{
        if (flags & LD_SUP_EXTRACTED)
                indent = 4;
        else
                indent = 2;
 
        (void) printf("%*sfile: %s¥n", indent, "", name);
}
 
void
ld_section(const char * name, Elf32_Shdr * shdr, Elf32_Word sndx,
    Elf_Data * data, Elf * elf)
{
        Elf32_Ehdr *    ehdr = elf32_getehdr(elf);
 
        if (ehdr->e_type == ET_REL)
                (void) printf("%*s   section [%ld]: %s¥n", indent,
                    "", sndx, name);
}

このサポートライブラリは、libelf に依存して、入力ファイルタイプを判定するために使用される ELF アクセス関数 elf32_getehdr(3ELF) を提供します。サポートライブラリは、次の行によって構築されます。


$ cc -o support.so.1 -G -K pic support.c -lelf -lc

次の例は、再配置可能オブジェクトおよび局所範囲アーカイブライブラリによる簡易アプリケーションの構築の結果生じたセクション診断を示しています。 -S オプションを使用すると、デフォルトデバッグ情報処理だけでなく、サポートライブラリの呼び出しも行われます。


$ LD_OPTIONS="-S./support.so.1 -Slibldstab.so.1" ¥ 
cc -o prog main.c -L. -lfoo

output image: prog
  file: /opt/COMPILER/crti.o
     section [1]: .shstrtab
     section [2]: .text
     .......
  file: /opt/COMPILER/crt1.o
     section [1]: .shstrtab
     section [2]: .text
     .......
  file: /opt/COMPILER/values-xt.o
     section [1]: .shstrtab
     section [2]: .text
     .......
  file: main.o
     section [1]: .shstrtab
     section [2]: .text
     .......
  file: ./libfoo.a
    file: ./libfoo.a(foo.o)
       section [1]: .shstrtab
       section [2]: .text
       .......
  file: /usr/lib/libc.so
  file: /opt/COMPILER/crtn.o
     section [1]: .shstrtab
     section [2]: .text
     .......
  file: /usr/lib/libdl.so.1

注 -

この例で表示されるセクションの数は、出力を簡素化するために減らされています。また、コンパイラドライバによって取り込まれるファイルも異なる場合があります。