リンカーとライブラリ

第 6 章 サポートインタフェース

リンカーには、リンカーおよび実行時リンカーの処理を監視し、場合によって変更を可能にするための多数のサポートインタフェースがあります。これらのインタフェースでは、通常、前の章で説明したよりもさらに詳しくリンク編集の概念を理解する必要があります。この章では、次のインタフェースについて説明します。

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

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

このセクションでは、入力ファイル検査用、およびリンク編集を構成するファイルの入力ファイルデータ変更用にもある程度サポートされている、「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

注 –

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


実行時リンカーの監査インタフェース

「rtld-監査」インタフェースを使用すると、プロセスがそれ自身に関する実行時リンク情報にアクセスできます。共有オブジェクトのプロファイリングでは、この機構の使用例として、共有オブジェクトの実行時プロファイルを示します。

「rtld-監査」インタフェースは、1 つまたは複数の監査インタフェースルーチンを提供する監査ライブラリとして実装されます。このライブラリがプロセスの一部として読み込まれている場合は、プロセス実行の各段階で、実行時リンカーによって監査ルーチンが呼び出されます。これらのインタフェースを使用すると、監査ライブラリは次のものにアクセスできます。

これらの機能のいくつかは、特殊な共有オブジェクトを事前に読み込むことによって実現できます。しかし、事前に読み込まれたオブジェクトは、プロセスのオブジェクトと同じ名前空間内に存在します。このため、通常、事前に読み込まれた共有オブジェクトの実装は制限されるか、複雑になります。「rtld-監査」インタフェースは、ユーザーに対して、監査ライブラリを実行するための固有の名前空間を提供します。この名前空間により、監査ライブラリがプロセス内で発生する通常の結合を妨害することはなくなります。

名前空間の確立

実行時リンカーは、動的実行可能なプログラムをその依存関係と結合すると、リンクマップのリンクリストを生成して、プロセスを記述します。リンクマップ構造は /usr/include/sys/link.h に定義されており、プロセス内の各オブジェクトを記述します。アプリケーションのオブジェクトを結合するために必要な記号検索機構は、このリンクマップリストを検索します。このリンクマップリストは、プロセスシンボル解決用の名前空間を提供します。

実行時リンカー自体も、リンクマップによって記述されます。このリンクマップは、アプリケーションオブジェクトのリストとは異なるリストで管理されます。この結果、実行時リンカーが固有の名前空間内に常駐することになるため、実行時リンカー内のサービスにアプリケーションが直接結合されることはなくなります。アプリケーションは、フィルタ libdl.so.1 を介して、実行時リンカーの公開サービスを呼び出すことができるだけです。

「rtld-監査」インタフェースは、すべての監査ライブラリを保持するための各自のリンクマップリストを使用します。このため、監査ライブラリは、アプリケーションのシンボル結合条件から分離されます。アプリケーションリンクマップリストの検査は、dlmopen(3DL) によって実行できます。dlmopen(3DL)RTLD_NOLOAD フラグとともに使用すると、 監査ライブラリで、オブジェクトを読み込むことなくその存在を照会することができます。

アプリケーションと実行時リンカーのリンクマップリストを定義するために、2 つの識別子が /usr/include/link.h に定義されています。

#define LM_ID_BASE      0     /* application link-map list */
#define LM_ID_LDSO      1     /* runtime linker link-map list */

各「rtld-監査」サポートライブラリには、固有の新しいリンクマップ識別子が割り当てられています。

監査ライブラリの作成

監査ライブラリは他の共有オブジェクトと同様に構築されます。プロセス内の固有名前空間には、いくつかの注意が必要です。

監査ライブラリが printf(3C) を呼び出す場合、その監査ライブラリは、libc への依存関係を定義する必要があります。共有オブジェクト出力ファイルの生成を参照してください。監査ライブラリには、固有の名前空間があるため、監査中のアプリケーションに存在する libc によって記号参照を満たすことはできません。監査ライブラリに libc への依存関係がある場合は、2 つのバージョンの libc.so.1 がプロセスに読み込まれます。1 つはアプリケーションのリンクマップリストの結合条件を満たし、もう 1 つは監査リンクマップリストの結合条件を満たします。

すべての依存関係が記録された状態で監査ライブラリが構築されるようにするには、リンカーの -z defs オプションを使用します。

システムインタフェースの中には、自らがプロセス内部の実装の唯一のインスタンスであると想定して存在するものがあります (シグナルや malloc(3C) など)。このようなインタフェースを使用すると、アプリケーションの動作が不正に変更されるおそれがあるため、監査ライブラリでは、このようなインタフェースの使用を避ける必要があります。


注 –

監査ライブラリは、mapmalloc(3MALLOC) を使用してメモリー割り当てを行うことができます。これは、アプリケーションによって通常使用される割り当てスキーマとこの割り当てが共存可能なためです。


監査インタフェースの呼び出し

「rtld-監査」インタフェースは、次のいずれかの方法によって有効になります。それぞれの方法は、監視対象のオブジェクトの範囲を意味します。

それぞれの呼び出し方法は、 dlmopen(3DL) によって読み込まれる共有オブジェクトをコロンで区切ったリストを含む文字列で構成されています。各オブジェクトは、各自の監査リンクマップリストに読み込まれます。また、各オブジェクトは、dlsym(3DL) によって、監査ルーチンがないか検索されます。 検出された監査ルーチンは、アプリケーション実行中に各段階で呼び出されます。

「rtld-監査」インタフェースを使用すると、複数の監査ライブラリを指定することができます。この方法で使用される監査ライブラリは、通常実行時リンカーによって返される結合を変更することはできません。これらの結合を変更すると、後に続く監査ライブラリで予期しない結果が生じる場合があります。

安全なアプリケーションは、トラストディレクトリからだけ監査ライブラリを取得できます。デフォルトでは、実行時リンカーが認識する 32 ビットオブジェクト用の唯一のトラストディレクトリは、/usr/lib/secure です。64 ビットオブジェクト用のトラストディレクトリは /usr/lib/secure/64 です。

ローカル監査の記録

ローカル監査要求は、オブジェクトがリンカーオプション -p または -P を使用して作成された場合に確立できます。たとえば、監査ライブラリ audit.so.1 を使用して libfoo.so.1 を監査するには、リンク編集時に -p オプションを使用して、この要求を記録します。


$ cc -G -o libfoo.so.1 -Wl,-paudit.so.1 -K pic foo.c
$ dump -Lv libfoo.so.1 | fgrep AUDIT
[3]    AUDIT      audit.so.1

実行時には、この監査識別子があることにより監査ライブラリが読み込まれ、識別するオブジェクトに関する情報がその監査ライブラリに渡されます。

この仕組みだけでは、識別するオブジェクトの検索などの情報は監査ライブラリが読み込まれる前に発生してしまいます。できるだけ多くの監査情報を提供するため、ローカル監査を要求するオブジェクトの存在は、そのオブジェクトのユーザーに広く知らされます。たとえば、libfoo.so.1 に依存するアプリケーションを作成すると、そのアプリケーションは、その依存関係の監査が必要であることを示すよう認識されます。


$ cc -o main main.c libfoo.so.1
$ dump -Lv main | fgrep AUDIT
[5]    DEPAUDIT   audit.so.1

この機構で監査が有効になると、アプリケーションのすべての明示的な依存関係に関する情報が監査ライブラリに渡されます。この依存関係の監査は、リンカーの -P オプションを使用することにより、オブジェクトの作成時に直接記録することもできます。


$ cc -o main main.c -Wl,-Paudit.so.1
$ dump -Lv main | fgrep AUDIT
[5]    DEPAUDIT   audit.so.1


注 –

環境変数 LD_NOAUDIT をヌル以外の値に設定すると、実行時に監査を無効にすることができます。


監査インタフェースの関数

次の関数が「rtld-監査」インタフェースによって提供されており、使用順序に従って記載されています。


注 –

アーキテクチャあるいはオブジェクトクラス固有のインタフェースの参照では、説明を簡潔にするため、省略して一般名を使用します。たとえば、la_symbind32() および la_symbind64()la_symbind() で表します。


la_version()

この関数は、実行時リンカーと監査ライブラリの間に初期ハンドシェークを提供します。監査ライブラリが読み込まれるためには、このインタフェースが提供されている必要があります。

uint_t la_version(uint_t version);

実行時リンカーは、サポート可能な最新バージョンの「rtld-監査」によって、このインタフェースを呼び出します。監査ライブラリは、このバージョンが十分に使用できるかどうかを確認して、使用する予定のバージョンを返すことができます。このバージョンは、通常、/usr/include/link.h に定義されている LAV_CURRENT です。

監査ライブラリがゼロ、あるいは、実行時リンカーがサポートする「rtld-監査」インタフェースよりも大きなバージョンを返す場合、監査ライブラリは破棄されます。

la_activity()

この関数は、リンク対応付けアクティビティが行われていることを監査プログラムに知らせます。

void la_activity(uintptr_t * cookie, uint_t flags);

cookie は、リンク対応付けの先頭のオブジェクトを指します。flags は、/usr/include/link.h に定義されているものと同じタイプのアクティビティを指します。

  • LA_ACT_ADD – リンク対応付けリストにオブジェクトが追加される

  • LA_ACT_DELETE – リンク対応付けリストからオブジェクトが削除される

  • LA_ACT_CONSISTENT – オブジェクトのアクティビティが完了した

la_objsearch()

この関数は、オブジェクトの検索を実行することを監査プログラムに知らせます。

char * la_objsearch(const char * name, uintptr_t * cookie, uint_t flags);

name は、検索中のファイルあるいはパス名を指します。cookie は、検索を開始しているオブジェクトを指します。flags は、/usr/include/link.h に定義されている name の出所および作成を示します。

  • LA_SER_ORIG – 初期検索名。通常は、DT_NEEDED エントリとして記録されたファイル名、あるいは dlmopen(3DL) に与えられた引数を指す

  • LA_SER_LIBPATH – パス名が LD_LIBRARY_PATH コンポーネントから作成されている

  • LA_SER_RUNPATH – パス名が「実行パス」コンポーネントから作成されている

  • LA_SER_DEFAULT – パス名がデフォルトの検索パスコンポーネントから作成されている

  • LA_SER_CONFIG – パスコンポーネントの出所が構成ファイルである。crle(1) のマニュアルページを参照

  • LA_SER_SECURE – パスコンポーネントがセキュアオブジェクトに固有である

戻り値は、実行時リンカーが処理を継続する必要がある検索パス名を示します。値 0 は、このパスが無視されることを示しています。検索パスを監視する監査ライブラリは、name を返します。

la_objopen()

この関数は、新しいオブジェクトが実行時リンカーによって読み込まれるたびに呼び出されます。

uint_t la_objopen(Link_map * lmp, Lmid_t lmid, uintptr_t * cookie);

lmp は、新しいオブジェクトを記述するリンクマップ構造を提供します。lmid は、オブジェクトが追加されているリンクマップリストを特定します。cookie は、識別子へのポインタを提供します。この識別子は、オブジェクト lmp に初期設定されます。この識別子は、監査ライブラリによって、オブジェクトをほかの「rtld-監査」インタフェースルーチンに対して特定するように変更できます。

la_objopen() 関数は、このオブジェクトで問題になるシンボル結合を示す値を返します。これらの値により、後の la_symbind() 呼び出しが生じます。この結果の値は、/usr/include/link.h に定義された次の値のマスクです。

  • LA_FLG_BINDTO – このオブジェクトに対する監査シンボル結合

  • LA_FLG_BINDFROM – このオブジェクトからの監査シンボル結合

これらの 2 つのフラグの使用法については、la_symbind() 関数を参照してください。

ゼロの戻り値は、結合情報がこのオブジェクトで問題にならないことを示します。

la_preinit()

この関数は、すべてのオブジェクトがアプリケーションに読み込まれた後で、アプリケーションへの制御の譲渡が発生する前に一度呼び出されます。

void la_preinit(uintptr_t * cookie);

cookie は、プロセスを開始したプライマリオブジェクト、通常は動的実行可能プログラムを表します。

la_symbind()

この関数は、la_objopen() によって結合通知のタグが付けられた 2 つのオブジェクト間で結合が発生すると呼び出されます。

uintptr_t la_symbind32(Elf32_Sym * sym, uint_t ndx,
        uintptr_t * refcook, uintptr_t * defcook, uint_t * flags);
 
uintptr_t la_symbind64(Elf64_Sym * sym, uint_t ndx,
        uintptr_t * refcook, uintptr_t * defcook, uint_t * flags,
        const char * sym_name);

sym は構築された記号構造であり、 sym->st_value は結合された記号定義のアドレスを示します。/usr/include/sys/elf.h を参照してください。la_symbind32() は、sym->st_name を調整して実際の記号名を指すようにしています。la_symbind64()sym->st_name を結合オブジェクトの文字列テーブルのインデックスのままにしています。

ndx は、結合オブジェクト動的記号テーブル内の記号インデックスを示します。refcook は、この記号への参照を行うオブジェクトを記述します。この識別子は、LA_FLG_BINDFROM を返した la_objopen() 関数に渡されたものと同じです。defcook は、この記号を定義するオブジェクトを記述します。この識別子は、LA_FLG_BINDTO を返した la_objopen() に渡されるものと同じです。

flags は、結合に関する情報を伝達できるデータ項目を指します。このデータ項目を使用すると、プロシージャのリンクテーブルシンボルエントリの連続監査を変更できます。この値は、/usr/include/link.h に定義された次のフラグのマスクです。

  • LA_SYMB_NOPLTENTERla_pltenter() 関数は、この記号に対しては呼び出されない

  • LA_SYMB_NOPLTEXITla_pltexit() 関数は、この記号に対しては呼び出されない

  • LA_SYMB_DLSYMdlsym(3DL) を呼び出した結果発生したシンボル結合

  • LA_SYMB_ALTVALUE (LAV_VERSION2)la_symbind() への以前の呼び出しによって、記号値に対して代替値が返される

デフォルトでは、la_pltenter() または la_pltexit() 関数が存在する場合、これらの関数は、シンボルが参照されるたびに、la_symbind() 関数の後で、プロシージャのリンクテーブルエントリに対して呼び出されます。詳細は、監査インタフェースの制限を参照してください。

戻り値は、この呼び出しに続いて制御を渡す必要があるアドレスを示します。シンボル結合を監視するだけの監査ライブラリは、sym->st_value の値を返すため、制御は結合記号定義に渡されます。監査ライブラリは、異なる値を返すことによって、シンボル結合を意図的にリダイレクトできます。

sym_name は、la_symbind64() のみに適用可能であり、処理されるシンボルの名前を含みます。この名前は、32 ビットインタフェースから sym->st_name フィールドで使用できます。

la_pltenter()

これらの関数はそれぞれ SPARC および x86 システムで呼び出されます。これらの関数は、結合通知のタグが付いた 2 つのオブジェクト間でプロシージャのリンクテーブルエントリが呼び出されるときに呼び出されます。

uintptr_t la_sparcv8_pltenter(Elf32_Sym * sym, uint_t ndx,
        uintptr_t * refcook, uintptr_t * defcook,
        La_sparcv8_regs * regs, uint_t * flags);
 
uintptr_t la_sparcv9_pltenter(Elf64_Sym * sym, uint_t ndx,
        uintptr_t * refcook, uintptr_t * defcook,
        La_sparcv9_regs * regs, uint_t * flags,
        const char * sym_name);
 
uintptr_t la_i86_pltenter(Elf32_Sym * sym, uint_t ndx,
        uintptr_t * refcook, uintptr_t * defcook,
        La_i86_regs * regs, uint_t * flags);

symndxrefcookdefcook、および sym_name は、la_symbind() に渡されたものと同じ情報を提供します。

regs は、/usr/include/link.h に定義されているように、SPARC システム上の out レジスタと、x86 システム上の stack および frame レジスタを指します。

flags は、結合に関する情報を伝達できるデータ項目を指します。このデータ項目を使用すると、プロシージャのリンクテーブルシンボルエントリの連続監査を変更できます。このデータ項目は、la_symbind() から flags によって指されるものと同じです。この値は、/usr/include/link.h に定義された次のフラグのマスクです。

  • LA_SYMB_NOPLTENTERla_pltenter() は、この記号では再び呼び出されることはない

  • LA_SYMB_NOPLTEXITla_pltexit() は、この記号では呼び出されない

戻り値は、この呼び出しに続いて制御を渡す必要があるアドレスを示します。シンボル結合を監視するだけの監査ライブラリは、sym->st_value の値を返すため、制御は結合記号定義に渡されます。監査ライブラリは、異なる値を返すことによって、シンボル結合を意図的にリダイレクトできます。

la_pltexit()

この関数は、結合通知のタグが付いた 2 つのオブジェクト間でプロシージャのリンクテーブルエントリが返されるときに呼び出されます。この関数は、制御が呼び出し側に到達する前に呼び出されます。

uintptr_t la_pltexit(Elf32_Sym * sym, uint_t ndx, uintptr_t * refcook,
        uintptr_t * defcook, uintptr_t retval);

uintptr_t la_pltexit64(Elf64_Sym * sym, uint_t ndx, uintptr_t * refcook,
        uintptr_t * defcook, uintptr_t retval, const char * sym_name);

symndxrefcookdefcook、および sym_name は、la_symbind() に渡されたものと同じ情報を提供します。retval は結合関数からの戻りコードです。シンボル結合を監視する監査ライブラリは、retval を返します。監査ライブラリは、意図的に異なる値を返すことができます。


注 –

la_pltexit() は実験段階のインタフェースです。詳細は、監査インタフェースの制限を参照してください。


la_objclose()

この関数はオブジェクトに対する終了コードが実行されてから、オブジェクトが読み込みを解除されるまでに呼び出されます。

uint_t la_objclose(uintptr_t * cookie);

cookie はオブジェクトを特定するもので、以前の la_objopen() から取得されています。戻り値は、ここではすべて無視されます。

監査インタフェースの例

次の単純な例では、動的実行可能プログラム date(1) によって読み込まれた各共有オブジェクトの依存関係の名前を出力する、監査ライブラリを作成しています。


$ cat audit.c
#include        <link.h>
#include        <stdio.h>
 
uint_t
la_version(uint_t version)
{
        return (LAV_CURRENT);
}
 
uint_t
la_objopen(Link_map * lmp, Lmid_t lmid, uintptr_t * cookie)
{
        if (lmid == LM_ID_BASE)
                (void) printf("file: %s loaded\n", lmp->l_name);
        return (0);
}
$ cc -o audit.so.1 -G -K pic -z defs audit.c -lmapmalloc -lc
$ LD_AUDIT=./audit.so.1 date
file: date loaded
file: /usr/lib/libc.so.1 loaded
file: /usr/lib/libdl.so.1 loaded
file: /usr/lib/locale/en_US/en_US.so.2 loaded
Thur Aug  10 17:03:55 PST 2000

監査インタフェースのデモンストレーション

/usr/demo/link_auditSUNWosdem パッケージには、「rtld-監査」インタフェースを使用する多数のデモアプリケーションが用意されています。

sotruss

このデモアプリケーションは、指定アプリケーションの動的オブジェクト間でのプロシージャ呼び出しを追跡します。

whocalls

このデモアプリケーションは、指定アプリケーションに呼び出されるたびに、指定関数のスタックと追跡を行います。

perfcnt

このデモアプリケーションは、指定アプリケーションの各関数で費やされた時間を追跡します。

symbindrep

このデモアプリケーションは、指定アプリケーションを読み込むために実行されたすべてのシンボル結合を報告します。

sotruss(1)whocalls(1) は、SUNWtoo パッケージに組み込まれています。perfcntsymbindrep はサンプルプログラムです。これらは、実際の環境での使用を目的としていません。

監査インタフェースの制限

la_pltexit() 系列の使用にはいくつかの制限があります。これらの制限は、呼び出し側と「呼び出し先」の間で余分なスタックフレームを挿入して、la_pltexit() 戻り値を提供するための必要から生じたものです。la_pltenter() ルーチンだけを呼び出す場合、この条件は問題になりません。この場合、目的の関数に制御を渡す前に、余分なスタックを整理できます。

これらの制限が原因で、la_pltexit() は、実験的インタフェースとみなされます。問題がある場合には、la_pltexit() ルーチンの使用は避けてください。

スタックを直接検査する関数

スタックを直接検査するか、またはその状態について仮定をたてる少数の関数があります。これらの関数の例としては、setjmp(3C) ファミリ、vfork(2)、および構造へのポインタではなく構造を返す関数があります。これらの関数は、la_pltexit() をサポートするために作成される余分なスタックによって調整されます。

実行時リンカーは、このタイプの関数を検出できないため、監査ライブラリの作成元が、このようなルーチンの la_pltexit() を無効にする必要があります。

実行時リンカーのデバッガインタフェース

実行時リンカーは、メモリーへのオブジェクトの割り当てやシンボルの結合を含む多数の操作を実行します。デバッグプログラムは、通常、これらの実行時リンカーの操作をアプリケーション解析の一部として記述する情報にアクセスする必要があります。これらのデバッグプログラムは、解析対象のアプリケーションに対する独立したプロセスとして実行されます。

このセクションでは、ほかのプロセスから動的にリンクされたアプリケーションを監視、変更する「rtld-デバッガ」インタフェースについて説明します。このインタフェースのアーキテクチャは、libthread_db(3THR) で使用されるモデルに従っています。

「rtld-デバッガ」インタフェースを使用する場合は、少なくとも次の 2 つのプロセスが関与します。

「rtld-デバッガ」は、制御プロセスがデバッガであり、そのターゲットが動的実行可能なプログラムの場合に、最もよく使用されます。

「rtld-デバッガ」インタフェースは、ターゲットプロセスに対して、次のアクティビティを有効にします。

制御プロセスとターゲットプロセス間の対話

ターゲットプロセスを検査して操作できるようにするために、「rtld-デバッガ」インタフェースは、エクスポートされたインタフェース、インポートされたインタフェース、およびエージェントを使用して、これらのインタフェース間で通信を行います。

制御プロセスは、librtld_db.so.1 によって提供される「rtld-デバッガ」インタフェースにリンクされて、このライブラリからエクスポートされたインタフェースを要求します。このインタフェースは、/usr/include/rtld_db.h に定義されています。次に、librtld_db.so.1 は制御プロセスからインポートされたインタフェースを要求します。この対話によって、「rtld-デバッガ」インタフェースは、次のことを行うことができます。

インポートされたインタフェースは多数の proc_service ルーチンから構成されます。大半のデバッガは、このルーチンをすでに使用してプロセスを解析しています。これらのルーチンについては、デバッガインポートインタフェースを参照してください。

「rtld-デバッガ」インタフェースは、「rtld-デバッガ」インタフェースの要求により解析中のプロセスが停止することを前提としています。停止しない場合は、ターゲットプロセスの実行時リンカー内にあるデータ構造が、検査時に一貫した状態にない可能性があります。

librtld_db.so.1、制御プロセス (デバッガ)、およびターゲットプロセス (動的実行可能プログラム) 間の情報の流れを、次の図に示します。

図 6–1 「rtld-デバッガ」の情報の流れ

rtld-デバッガの情報の流れ。


注 –

「rtld-デバッガ」インタフェースは、実験的と見なされる proc_service インタフェース (/usr/include/proc_service.h) に依存します。「rtld-デバッガ」インタフェースは、展開時に、proc_service インタフェース内の変更を追跡しなければならないことがあります。


「rtld-デバッガ」インタフェースを使用する制御プロセスのサンプル実装状態は、/usr/demo/librtld_dbSUNWosdem パッケージに用意されています。 このデバッガ rdb は、proc_service インポートインタフェースの使用例、およびすべての librtld_db.so.1 エクスポートインタフェースの必須呼び出しシーケンスを示します。次のセクションでは、「rtld-デバッガ」インタフェースについて説明します。さらに詳しい情報は、サンプルデバッガをテストして入手することができます。

デバッガインタフェースのエージェント

エージェントは、内部インタフェース構造を記述可能な不透明なハンドルを提供します。エージェントは、エクスポートインタフェースとインポートインタフェースとの間の通信機構も提供します。「rtld-デバッガ」インタフェースは、いくつかのプロセスを同時に操作できるデバッガによる使用を目的としているため、これらのエージェントは、プロセスを特定するために使用されます。

struct ps_prochandle

制御プロセスによって、エクスポートインタフェースとインポートインタフェースの間で渡されるターゲットプロセスを特定するために作成される不透明な構造です。

struct rd_agent

「rtld-デバッガ」インタフェースによって、エクスポートインタフェースとインポートインタフェースの間で渡されるターゲットプロセスを特定するために作成される不透明な構造です。

デバッガエクスポートインタフェース

このセクションでは、/usr/lib/librtld_db.so.1 監査ライブラリによってエクスポートされるさまざまなインタフェースについて説明します。機能グループごとに分けて説明します。

エージェント操作インタフェース

rd_init()

この関数は、「rtld-デバッガ」バージョン条件を確立します。ベースとなるバージョンは、RD_VERSION1 として定義されています。現在の「バージョン」は常に RD_VERSION で定義されます。

rd_err_e rd_init(int version);

Solaris 8 10/00 で追加されたバージョン RD_VERSION2 は、rd_loadobj_t 構造体を拡張するものです。詳細は、読み込み可能オブジェクトの走査rl_flags rl_bend、および rl_dynamic フィールドを参照してください。

Solaris 8 01/01 で追加されたバージョン RD_VERSION3 は、rd_plt_info_t 構造体を拡張するものです。詳細は、プロシージャのリンクテーブルのスキップpi_baddr および pi_flags フィールドを参照してください。

制御プロセスのバージョン条件が使用可能な「rtld-デバッガ」インタフェースよりも大きい場合は、RD_NOCAPAB が返されます。

rd_new()

この関数は、新しいエクスポートのインタフェースエージェントを作成します。

rd_agent_t * rd_new(struct ps_prochandle * php);

php は、制御プロセスによってターゲットプロセスを特定するために作成された cookie です。この cookie は、制御プロセスによってコンテキストを維持するために提供される重要なインタフェースで使用されるものであり、「rtld-デバッガ」インタフェースに対して不透明です。

rd_reset()

この関数は、rd_new() に指定された同じ ps_prochandle 構造に基づくエージェント内の情報をリセットします。

rd_err_e rd_reset(struct rd_agent * rdap);

この関数は、ターゲットプロセスが再スタートされると呼び出されます。

rd_delete()

この関数は、エージェントを削除し、それに関連するすべての状態を解除します。

void rd_delete(struct rd_agent * rdap);

エラー処理

次のエラー状態は、「rtld-デバッガ」インタフェース (rtld_db.h に定義) によって返されます。

typedef enum {
        RD_ERR,
        RD_OK,
        RD_NOCAPAB,
        RD_DBERR,
        RD_NOBASE,
        RD_NODYNAM,
        RD_NOMAPS
} rd_err_e;

次のインタフェースは、エラー情報を収集するために使用できます。

rd_errstr()

この関数は、エラーコード「rderr」を記述する記述エラー文字列を返します。

char * rd_errstr(rd_err_e rderr);
rd_log()

この関数は、ログ記録をオン (1) またはオフ (0) にします。


void rd_log(const int onoff);

ログ記録がオンの場合、制御プロセスによって提供されるインポートインタフェース関数 ps_plog() は、さらに詳しい診断情報によって呼び出されます。

読み込み可能オブジェクトの走査

実行時リンカーのリンクマップで維持される各オブジェクト情報の取得は、rtld_db.h に定義された次の構造を使用して実現されます。

typedef struct rd_loadobj {
        psaddr_t        rl_nameaddr;
        unsigned        rl_flags;
        psaddr_t        rl_base;
        psaddr_t        rl_data_base;
        unsigned        rl_lmident;
        psaddr_t        rl_refnameaddr;
        psaddr_t        rl_plt_base;
        unsigned        rl_plt_size;
        psaddr_t        rl_bend;
        psaddr_t        rl_padstart;
        psaddr_t        rl_padend;
        psaddt_t        rl_dynamic;
} rd_loadobj_t;

文字列ポインタを含めて、この構造で指定されるアドレスはすべてターゲットプロセス内のアドレスであり、制御プロセス自体のアドレス空間のアドレスでないことに注意してください。

rl_nameaddr

動的オブジェクトの名前を含む文字列へのポインタ

rl_flags

リビジョン RD_VERSION2 では、動的に読み込まれる再配置可能オブジェクトは RD_FLG_MEM_OBJECT で識別されます。

rl_base

動的オブジェクトの基本アドレス

rl_data_base

動的オブジェクトのデータセグメントの基本アドレス

rl_lmident

リンクマップ識別子 (名前空間の確立を参照)

rl_refnameaddr

動的オブジェクトが標準フィルタの場合は、「フィルティー」の名前を指定する

rl_plt_baserl_plt_size

これらの要素は、下方互換性のために存在するものであり、現在は使用されていない

rl_bend

オブジェクトのエンドアドレス (text + data + bss)。リビジョン RD_VERSION2 では、動的に読み込まれる再配置可能オブジェクトの場合、この要素は作成されたオブジェクトの最後を指します。このオブジェクトには、自身のセクションヘッダーが含まれています。

rl_padstart

動的オブジェクト前のパッドの基本アドレス (動的オブジェクトのパッドを参照)

rl_padend

動的オブジェクト後のパッドの基本アドレス (動的オブジェクトのパッドを参照)

rl_dynamic

このフィールドは RD_VERSION2 に追加されたもので、DT_CHECKSUM (表 7–42 を参照) のエントリへの参照を可能にするオブジェクトの動的セクションのベースアドレスを提供します。

rd_loadobj_iter() ルーチンは、このオブジェクトデータ構造を使用して実行時リンカーのリンクマップリストの情報にアクセスします。

rd_loadobj_iter()

この関数は、ターゲットプロセスに現在読み込まれている動的オブジェクトすべてを反復します。

typedef int rl_iter_f(const rd_loadobj_t *, void *);
 
rd_err_e rd_loadobj_iter(rd_agent_t * rap, rl_iter_f * cb,
        void * clnt_data);

各反復時に、cb によって指定されたインポート関数が呼び出されます。clnt_data は、cb 呼び出しにデータを渡すために使用できます。各オブジェクトに関する情報は、スタックが割り当てられた volatile rd_loadobj_t 構造へのポインタを介して返されます。

cb ルーチンからの戻りコードは、rd_loadobj_iter() によってテストされ、次の意味を持ちます。

  • 1 – リンクマップの処理を継続

  • 0 – リンクマップの処理を停止して、制御プロセスに制御を返す

rd_loadobj_iter() は、正常だと RD_OK を返します。RD_NOMAPS が返される場合、実行時リンカーは、まだ初期リンクマップを読み込みません。

イベント通知

制御プロセスは、実行時リンカーの適用範囲内で発生する特定のイベントを追跡できます。これらのイベントは次のとおりです。

RD_PREINIT

実行時リンカーは、すべての動的オブジェクトを読み込んで再配置し、読み込まれた各オブジェクトの .init セクションの呼び出しを開始する。

RD_POSTINIT

実行時リンカーは、すべての .init セクションの呼び出しを終了して、基本実行可能プログラムに制御を渡す。

RD_DLACTIVITY

実行時リンカーは、動的オブジェクトを読み込みまたは読み込み解除のために呼び出される。

これらのイベントは、次のインタフェース (sys/link.hrtld_db.h に定義) を使用して監視できます。

typedef enum {
        RD_NONE = 0,
        RD_PREINIT,
        RD_POSTINIT,
        RD_DLACTIVITY
} rd_event_e;
 
/*
 * イベント通知の方法
 */
typedef enum {
        RD_NOTIFY_BPT,
        RD_NOTIFY_AUTOBPT,
        RD_NOTIFY_SYSCALL
} rd_notify_e;
 
/*
 * イベント通知の方法についての情報
 */
typedef struct rd_notify {
        rd_notify_e     type;
        union {
                psaddr_t        bptaddr;
                long            syscallno;
        } u;
} rd_notify_t;

イベントを追跡する関数を次に示します。

rd_event_enable()

この関数は、イベント監視を有効 (1) または無効 (0) にします。

rd_err_e rd_event_enable(struct rd_agent * rdap, int onoff);

注 –

パフォーマンス上の理由から、現在、実行時リンカーはイベントの無効化を無視します。制御プロセスは、このルーチンへの最後の呼び出しが原因で指定のブレークポイントに到達しないと、想定することはできません。


rd_event_addr()

この関数は、制御プログラムへの指定イベントの通知方法を指定します。

rd_err_e rd_event_addr(rd_agent_t * rdap, rd_event_e event,
        rd_notify_t * notify);

イベントタイプに従って、制御プロセスの通知は、notify->u.syscallno で特定されるチープなシステム呼び出しを呼び出すか、または notify->u.bptaddr によって指定されたアドレスでブレークポイントを実行することで行われます。システム呼び出しの追跡または実際のブレークポイントの設定は、制御プロセスが行う必要があります。

イベントが発生した場合は、rtld_db.h に定義された次のインタフェースによって追加情報を取得できます。

typedef enum {
        RD_NOSTATE = 0,
        RD_CONSISTENT,
        RD_ADD,
        RD_DELETE
} rd_state_e;
 
typedef struct rd_event_msg {
        rd_event_e      type;
        union {
                rd_state_e      state;
        } u;
} rd_event_msg_t;

rd_state_e の値を次に示します。

RD_NOSTATE

使用可能な追加状態情報なし

RD_CONSISTANT

リンクマップは安定した状態にあってテスト可能

RD_ADD

動的オブジェクトは削除処理中であり、リンクマップは安定した状態にない。リンクマップは、RD_CONSISTANT 状態に達するまでテストできない

RD_DELETE

動的オブジェクトは削除処理中であり、リンクマップは安定した状態にない。リンクマップは、RD_CONSISTANT 状態に達するまでテストできない

rd_event_getmsg() 関数を使用して、このイベント状態情報を取得します。

rd_event_getmsg()

この関数は、イベントに関する追加情報を提供します。

rd_err_e rd_event_getmsg(struct rd_agent * rdap, rd_event_msg_t * msg);

次の表は、異なる各イベントタイプで可能な状態を示しています。

RD_PREINIT

RD_POSTINIT

RD_DLACTIVITY

RD_NOSTATE

RD_NOSTATE

RD_CONSISTANT

 

 

RD_ADD

 

 

RD_DELETE

プロシージャのリンクテーブルのスキップ

「rtld-デバッガ」インタフェースは、制御プロセスが、プロシージャのリンクのテーブルエントリをスキップオーバーする機能を提供します。デバッガなどの制御プロセスが、関数に介入するようにとの要求をはじめて受けると、プロシージャのリンクテーブル処理は、制御を実行時リンカーに渡して関数定義を検索します。

次のインタフェースを使用すると、制御プロセスで実行時リンカーのプロシージャのリンクテーブル処理にステップオーバーできます。制御プロセスは、ELF ファイルで提供される外部情報に基づいて、プロシージャのリンクのテーブルエントリに遭遇する時期を判定できます。

ターゲットプロセスは、プロシージャのリンクのテーブルエントリに介入すると、rd_plt_resolution() インタフェースを呼び出します。

rd_plt_resolution()

この関数は、現在のプロシージャのリンクテーブルエントリの解決状態と、それをスキップする方法に関する情報を返します。


rd_err_e rd_plt_resolution(rd_agent_t * rdap, paddr_t pc,
        lwpid_t lwpid, paddr_t plt_base, rd_plt_info_t * rpi);

pc は、プロシージャのリンクテーブルエントリの最初の命令を表します。lwpidlwp 識別子を提供し、plt_base はプロシージャのリンクテーブルの基本アドレスを提供します。これらの 3 つの変数は、各種のアーキテクチャがプロシージャのリンクテーブルを処理するため十分な情報を提供します。

rpi は、rtld_db.h 内の次のデータ構造に定義された、プロシージャのリンクのテーブルエントリに関する詳しい情報を提供します。

typedef enum {
        RD_RESOLVE_NONE,
        RD_RESOLVE_STEP,
        RD_RESOLVE_TARGET,
        RD_RESOLVE_TARGET_STEP
} rd_skip_e;
 
typedef struct rd_plt_info {
        rd_skip_e       pi_skip_method;
        long            pi_nstep;
        psaddr_t        pi_target;
        psaddr_t        pi_baddr;
        unsigned int    pi_flags;
} rd_plt_info_t;

#define RD_FLG_PI_PLTBOUND     0x0001

rd_plt_info_t 構造体の要素を次に示します。

pi_skip_method

プロシージャのリンクテーブルエントリがどのように扱われるかを示す。rd_skip_e 値内の 1 つに設定される

pi_nstep

RD_RESOLVE_STEP または RD_RESOLVE_TARGET_STEP が返された時にステップオーバーする命令がいくつあるかを示す

pi_target

RD_RESOLVE_TARGET_STEP または RD_RESOLVE_TARGET が返された時にブレークポイントを設定するアドレス指定する

pi_baddr

RD_VERSION3 で追加された、プロシージャのリンクテーブルの宛先アドレス。pi_flags フィールドの RD_FLG_PI_PLTBOUND フラグが設定されると、この要素は解決された (結合された) 宛先アドレスを示す

pi_flags

RD_VERSION3 で追加されたフラグフィールド。フラグ RD_FLG_PI_PLTBOUND は、pi_baddr フィールドで取得できる宛先アドレスへ解決された (結合された) プロシージャのリンクエントリを示す

次のシナリオは rd_plt_info_t 戻り値から考えられます。

動的オブジェクトのパッド

実行時リンカーのデフォルト動作は、オペレーティングシステムに依存して、最も効率的に参照できる場所に動的オブジェクトを読み込みます。制御プロセスの中には、ターゲットプロセスのメモリーに読み込まれたオブジェクトの周りにパッドがあることによって、利益を受けるものがあります。このインタフェースを使用すると、制御プロセスは、このパッドを要求できます。

rd_objpad_enable()

この関数は、ターゲットプロセスによって続けて読み込まれたオブジェクトのパッドを有効または無効にします。パッドは読み込まれたオブジェクトの両側で行われます。

rd_err_e rd_objpad_enable(struct rd_agent * rdap, size_t padsize);

padsize は、メモリーに読み込まれたオブジェクトの前後両方で維持されるパッドのサイズをバイト数で指定します。このパッドは、mmap(2) に対し PROT_NONE アクセス権と MAP_NORESERVE フラグを指定して、メモリー割り当てとして予約されます。実行時リンカーは、読み込まれたオブジェクトに隣接するターゲットプロセスの仮想アドレス空間の領域を効果的に予約します。これらの領域は、制御プロセスによって後に利用できます。

padsize を 0 にすると、後のオブジェクトに対するオブジェクトパッドは無効になります。


注 –

mmap(2)MAP_NORESERVE を指定して /dev/zero から取得したメモリー割り当ての予約は、proc(1) 機能や rd_loadobj_t で提供されるリンクマップ情報を参照することで得られます。


デバッガインポートインタフェース

制御プロセスが librtld_db.so.1 に対して提供しなければならないインポートインタフェースは、/usr/include/proc_service.h に定義されています。これらの proc_service 関数のサンプル実装状態は、rdb デモデバッガにあります。「rtld-デバッガ」インタフェースは、使用可能な proc_service インタフェースのサブセットだけを使用します。「rtld-デバッガ」インタフェースの今後のバージョンでは、互換性のない変更を作成することなく、追加 proc_service インタフェースを利用できる可能性があります。

次のインタフェースは、現在、「rtld-デバッガ」インタフェースによって使用されています。

ps_pauxv()

この関数は、auxv ベクトルのコピーへのポインタを返します。

ps_err_e ps_pauxv(const struct ps_prochandle * ph, auxv_t ** aux);

auxv ベクトル情報は、割り当てられた構造にコピーされるため、このポインタの存続期間は、ps_prochandle が有効な間になります。

ps_pread()

この関数は、ターゲットプロセスからデータを読み取ります。


ps_err_e ps_pread(const struct ps_prochandle * ph, paddr_t addr,
        char * buf, int size);

ターゲットプロセス内のアドレス addr から、size バイトが buf にコピーされます。

ps_pwrite()

この関数は、ターゲットプロセスにデータを書き込みます。

ps_err_e ps_pwrite(const struct ps_prochandle * ph, paddr_t addr,
        char * buf, int size);

buf から size バイトが、ターゲットプロセスのアドレス addr にコピーされます。

ps_plog()

この関数は、「rtld-デバッガ」インタフェースから追加診断情報によって呼び出されます。

void ps_plog(const char * fmt, ...);

この診断情報をどこに記録するか、または記録するかどうかは、制御プロセスが決める必要があります。ps_plog() の引数は、printf(3C) 形式に従います。

ps_pglobal_lookup()

この関数は、ターゲットプロセス内のシンボルを検索します。

ps_err_e ps_pglobal_lookup(const struct ps_prochandle * ph,
        const char * obj, const char * name, ulong_t * sym_addr);

ターゲットプロセス ph 内のオブジェクト obj 内で、シンボル name が検索されます。シンボルが検出されると、シンボルのアドレスが sym_addr に保存されます。

ps_pglobal_sym()

この関数は、ターゲットプロセス内のシンボルを検索します。

ps_err_e ps_pglobal_sym(const struct ps_prochandle * ph,
        const char * obj, const char * name, ps_sym_t * sym_desc);

ターゲットプロセス ph 内のオブジェクト obj 内で、シンボル name が検索されます。シンボルが検出されると、シンボルの記述子が sym_desc に保存されます。

「rtld-デバッガ」インタフェースがアプリケーションまたは実行時リンカー内の記号を検出してから、リンクマップを作成する必要があるイベントでは、obj に対する次の予約値を使用できます。

#define PS_OBJ_EXEC ((const char *)0x0)  /* アプリケーション id */
#define PS_OBJ_LDSO ((const char *)0x1)  /* 実行時リンカー id */

制御プロセスは、次の擬似コードを使用して、これらのオブジェクト用の procfs ファイルシステムを利用できます。

ioctl(.., PIOCNAUXV, ...)       - obtain AUX vectors
ldsoaddr = auxv[AT_BASE];
ldsofd = ioctl(..., PIOCOPENM, &ldsoaddr);
 
/* elf プロセス情報が ldsofd で検出される... */
 
execfd = ioctl(.., PIOCOPENM, 0);
 
/* elf プロセス情報が execfd で検出される... */

ファイル記述子が見つかったら、ELF ファイルは、制御プログラムによってその記号情報をテストできます。