リンカーとライブラリ

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

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

このセクションでは、入力ファイル検査用、およびリンク編集を構成するファイルの入力ファイルデータ変更用にもある程度サポートされている、「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) を使用してサポートインタフェースルーチンの検索が実行されます。これらのサポートルーチンは、リンク編集の各段階で呼び出されます。

サポートライブラリは、32 ビットまたは 64 ビットのいずれの場合でも、呼び出されるリンカーの ELF クラスと一致している必要があります。詳細は、32 ビットおよび 64 ビット環境 を参照してください。


注 –

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


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

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

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

SGS_SUPPORT 環境変数は、接尾辞 _32 または _64 を使用して指定できます。また、リンカーオプション - z ld32 および -z ld64 を使用して、-S オプション要件を定義できます。これらの各定義は、対応する 32 ビットまたは 64 ビットのリンカーによってのみ解釈されます。このため、リンカーの種類が不明な場合に、両方の種類のサポートライブラリを指定できます。

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

「ld-サポート」インタフェースはすべて、ヘッダーファイル link.h に定義されています。インタフェース引数はすべて、基本的な C タイプまたは ELF タイプです。 ELF データタイプは、ELF アクセスライブラリ libelf を使用して確認できます。libelf の詳細は、elf(3ELF) を参照してください。次のインタフェース関数が「ld-サポート」インタフェースにより提供されます。各インタフェース関数は、使用順序に従って記載されています。

ld_version()

この関数は、リンカーとサポートライブラリとの間の初期ハンドシェークを提供します。

uint_t ld_version(uint_t version);

リンカーは、サポート可能な最新バージョンの「ld-サポート」インタフェースを使用して、このインタフェースを呼び出します。サポートライブラリは、このバージョンが使用するのに十分かどうかを確認して、使用する予定のバージョンを返すことができます。通常、このバージョンは LD_SUP_VCURRENT です。

サポートライブラリがこのインタフェースを提供しない場合、初期サポートレベルは LD_SUP_VERSION1 と見なされます。

サポートライブラリがゼロのバージョン、またはリンカーがサポートする「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 は出力ファイルタイプであり、sys/elf.h に定義されている ET_DYNET_RELET_EXEC のいずれかです。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 は入力ファイルのタイプを示し、libelf.h に定義されているように ELF_K_AR または ELF_K_ELF のいずれかになります。flags は、リンカーによるファイルの取得方法を示し、次の 1 つまたは複数の定義にすることができます。

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

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

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

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

ld_input_section()

この関数は、入力ファイルの各セクションに対して呼び出されます。この関数は、リンカーがそのセクションを出力ファイルに送信することを決定する前に呼び出されます。これは、出力ファイルに寄与するセクションに対してのみ呼び出される、ld_section() 処理とは異なります。

void ld_input_section(const char * name, Elf32_Shdr ** shdr,
        Elf32_Word sndx, Elf_Data * data, Elf * elf, unit_t flags);

void ld_input_section64(const char * name, Elf64_Shdr ** shdr,
        Elf64_Word sndx, Elf_Data * data, Elf * elf, uint_t flags);

name は、入力セクション名を示します。shdr は、関連のセクションヘッダーへのポインタを示します。sndx は、入力ファイル内のセクションインデックスです。data は、関連データバッファへのポインタを示します。elf は、ファイル ELF 記述子へのポインタです。flags は、将来の使用のために予約されています。

セクションヘッダーの再割り当ておよび *shdr への代入によるセクションヘッダーの変更は許されています。リンカーは、ld_input_section() から戻った後で、*shdr が指し示すセクションヘッダー情報を使用して、セクションを処理します。

データを再割り当てし、Elf_Data バッファの d_buf ポインタに代入してデータを変更できます。データを変更する場合、Elf_Data バッファの d_size 要素を正しく設定しなければなりません。出力イメージの一部になる入力セクションでは、d_size 要素をゼロに設定すると、出力イメージからデータが実際に削除されます。

flags フィールドは、初期値にゼロが設定される uint_t データフィールドを指します。フラグは、将来のアップデートでリンカーやサポートライブラリが割り当てできるように提供はされていますが、現在のところは割り当てられていません。

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 オプションによって取り除かれるセクション、または SHT_SUNW_COMDAT 処理や SHF_EXCLUDE の識別によって破棄されるセクションは、ld_section() に報告されません。「COMDAT」セクション表 7–14 を参照してください。


ld_input_done()

この関数は、入力ファイルの処理が完了し、出力ファイルの配置が実行される前に呼び出されます。

void ld_input_done(uint_t flags);

flags フィールドは、初期値にゼロが設定される uint_t データフィールドを指します。フラグは、将来のアップデートでリンカーやサポートライブラリが割り当てできるように提供はされていますが、現在のところは割り当てられていません。

ld_atexit()

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

void ld_atexit(int status);

void ld_atexit64(int status);

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

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

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


$ cat support.c
#include        <link.h>
#include        <stdio.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,
                    "", (long)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

注 –

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