EN JA
LINK(5)
LINK(5) FreeBSD File Formats Manual LINK(5)

名称

linkダイナミックローダとリンクエディタインタフェース

書式

#include < sys/types.h>
#include < nlist.h>
#include < link.h>

解説

インクルードファイル < link.h> では、ダイナミックにリンクされたプログラムやライブラリに含まれる数種の構造体が宣言されています。その構造体は、リンクエディタとローダ機構のいくつかの構成要素間のインタフェースを定義します。バイナリ中でのこれらの構造体のレイアウトは、多くの点で a.out 形式に類似しており、シンボル定義 (付随する文字列テーブルを含む) や外部エンティティへの参照を解決するのに必要なリロケーションレコードといった、よく似た機能を提供します。それに加え、ダイナミックロードとリンク処理に固有のいくつかのデータ構造も記録しています。このようなデータ構造としては、リンクエディット処理を完結するのに必要な他のオブジェクトへの参照や、異なるプロセス間でコードページの共有を進めるための 位置独立コード (Position Independent Code 略して PIC) を機能させるための間接テーブルがあります。ここで述べるデータ構造全体を ランタイムリロケーションセクション (RRS) と呼び、ダイナミックにリンクされるプログラムや共有オブジェクトの標準テキスト及びデータセグメントに埋め込まれます。これは、既存の a.out(5) 形式には、RRS のための場所が他にないからです。

あるプログラムを実行可能とする処理が、システムリソースの使用を最適化しつつ正しく完了するよう、複数のユーティリティが協調して働きます。コンパイラは、PIC コードを出力し、それから ld(1) によって共有ライブラリが作られます。コンパイラは、また、初期化される各データアイテムのサイズ情報をアセンブラディレクティブ .size を用いて記録します。 PIC コードは、ある間接テーブルを通じてデータ変数にアクセスする点で従来のコードと異なっています。この表は、グローバルオフセットテーブルと呼ばれ、慣習によって、予約名 _GLOBAL_OFFSET_TABLE_ によってアクセス可能です。ここで用いられるメカニズムの詳細は、機種依存ですが、通常は、そのマシンのレジスタ 1 本がこの用途に予約されます。このような仕組みの背景にある合理性は、実際のロードアドレスとは、独立したコードを生成することです。実行時には、アドレス空間において様々な共有オブジェクトがロードされるアドレスに応じて、グローバルオフセットテーブルに含まれる値のみ変更すればよいのです。

同様に、大域的に定義された関数の呼び出しは、コアイメージのデータセグメント中に置かれているプロシージャリンケージテーブル (PLT) を通じて間接的に行われます。これもまた、実行時にテキストセグメントを修正せずに済ませるためのものです。

リンクエディタがグローバルオフセットテーブルとプロシージャリンケージテーブルを配置するのは、複数の PIC オブジェクトファイルを結合してプロセスのアドレス空間にマップするのに適した 1 つのイメージにする時です。リンクエディタは、また、実行時のリンクエディタが必要とする全てのシンボルを集め、それらをイメージのテキストとデータのビット列と共にストアします。もう 1 つの予約シンボル _DYNAMIC は、実行時のリンク構造が存在することを示すのに用いられます。 _DYNAMIC が 0 にリロケートされる場合は、実行時リンクエディタを起動する必要はありません。もし _DYNAMIC が非 0 なら、_DYNAMIC は、必要なリロケーション情報とシンボル情報の位置を引き出すことができるデータ構造を指しています。これは、特に、スタートアップモジュール crt0 で利用されます。慣習として、_DYNAMIC 構造体は、それが属するイメージのデータセグメントの最初に置かれます。

データ構造

ダイナミックリンクと実行時リロケーションをサポートするデータ構造は、それらの処理の適用対象イメージのテキスト及びデータセグメントの両方の中にあります。テキストセグメントには、シンボル記述や名前といった読み込み専用データが含まれ、他方データセグメントには、リロケーション処理で更新する必要のあるテーブル類が含まれます。

シンボル _DYNAMIC は、 _dynamic 構造体を参照します:

struct _dynamic { 
 int d_version; 
 struct  so_debug *d_debug; 
 union { 
  struct section_dispatch_table *d_sdt; 
 } d_un; 
 struct  ld_entry *d_entry; 
};
d_version
このフィールドは、異なったバージョンのダイナミックリンク実装用に提供されています。 ld(1) 及び ld.so(1) が理解する現在のバージョン番号は、 SunOS 4.x リリースで用いられている LD_VERSION_SUN (3) と、 FreeBSD 1.1 以来使用されている LD_VERSION_BSD (8) です。
d_un
d_version に応じたデータ構造を参照します。
so_debug
このフィールドは、共有オブジェクトのシンボルテーブルをアクセスするためのフックをデバッガに提供します。この共有オブジェクトは、実行時リンクエディタの処理の結果ロードされたものです。

section_dispatch_table 構造体がメインとなる“ディスパッチャ”テーブルであり、イメージ内で様々なシンボル情報やリロケーション情報が置かれるセグメントへのオフセットを保持します。

struct section_dispatch_table { 
 struct so_map *sdt_loaded; 
 long sdt_sods; 
 long sdt_filler1; 
 long sdt_got; 
 long sdt_plt; 
 long sdt_rel; 
 long sdt_hash; 
 long sdt_nzlist; 
 long sdt_filler2; 
 long sdt_buckets; 
 long sdt_strings; 
 long sdt_str_sz; 
 long sdt_text_sz; 
 long sdt_plt_sz; 
};
sdt_loaded
ロードされた最初のリンクマップ (後述) へのポインタ。このフィールドは、 ld.so によって設定されます。
sdt_sods
この オブジェクトが必要とする共有オブジェクト記述子の (リンク) リストの先頭。
sdt_filler1
使用しないで下さい (SunOS では、ライブラリの検索ルールを指定するのに使用されていました)。
sdt_got
このイメージ中でのグローバルオフセットテーブルの位置。
sdt_plt
このイメージ中でのプロシージャリンケージテーブルの位置。
sdt_rel
実行時のリロケーションを指定する relocation_info 構造体 ( a.out(5) 参照) の配列の位置。
sdt_hash
このオブジェクトのシンボルテーブルでシンボル検索を高速化するためのハッシュテーブルの位置。
sdt_nzlist
シンボルテーブルの位置。
sdt_filler2
現在使用されていません。
sdt_buckets
sdt_hash 中のバケット数。
sdt_strings
sdt_nzlist に対応するシンボル文字列テーブルの位置。
sdt_str_sz
文字列テーブルのサイズ。
sdt_text_sz
このオブジェクトのテキストセグメントのサイズ。
sdt_plt_sz
プロシージャリンケージテーブルのサイズ。

sod 構造体は、それを含むオブジェクトのリンクエディット処理を完了するのに必要な共有オブジェクトを記述します。そのようなオブジェクトのリスト ( sod_next で連結されます) は、section_dispatch_table 構造体の sdt_sods によって指し示されます。

struct sod { 
 long sod_name; 
 u_int sod_library : 1, 
  sod_reserved : 31; 
 short sod_major; 
 short sod_minor; 
 long sod_next; 
};
sod_name
このリンクオブジェクトを記述する文字列の、テキストセグメントにおけるオフセット。
sod_library
もし設定されていれば、 sod_name は、 ld.so が検索することになるライブラリを指定します。そのパス名は、あるディレクトリ群 ( ldconfig(8) 参照) で lib<sod_name>.so.n.m にマッチする共有オブジェクトを検索することで得られます。もし設定されていなければ、 sod_name は、希望する共有オブジェクトに対するフルパス名を指し示す必要があります。
sod_major
ロードすべき共有オブジェクトのメジャーバージョン番号を指定します。
sod_minor
ロードすべき共有オブジェクトの希望するマイナバージョン番号を指定します。

プロセスのアドレス空間にロードされる共有オブジェクト全てを追跡するために、実行時リンクエディタは、 リンクマップ と呼ばれる構造体のリストを管理しています。これらの構造体は、実行時にのみ用いられ、実行可能ファイルや共有ライブラリのテキストあるいはデータセグメントにはありません。

struct so_map { 
 caddr_t som_addr; 
 char  *som_path; 
 struct so_map *som_next; 
 struct sod *som_sod; 
 caddr_t som_sodbase; 
 u_int som_write : 1; 
 struct _dynamic *som_dynamic; 
 caddr_t som_spd; 
};
som_addr
このリンクマップに対応する共有オブジェクトがロードされるアドレス。
som_path
ロードされるオブジェクトのフルパス名。
som_next
次のリンクマップへのポインタ。
som_sod
この共有オブジェクトのロードをつかさどる sod 構造体。
som_sodbase
最近のバージョンの実行時リンカでは捨てられています。
som_write
このオブジェクトのテキストセグメント (の一部分) が現在書き込み可能である場合にセットされます。
som_dynamic
このオブジェクトの _dynamic 構造体へのポインタ。
som_spd
実行時リンクエディタが管理するプライベートデータと連結するためのフック。

サイズ付きシンボル記述。これは、単に nlist 構造体にフィールド ( nz_size) を 1 つ追加したものです。共有オブジェクトのデータセグメントにあるアイテムのサイズ情報を伝達するのに用いられます。この構造体の配列は、共有オブジェクトのテキストセグメントに存在し、そのアドレスは、 section_dispatch_tablesdt_nzlist フィールドで指定されます。

struct nzlist { 
 struct nlist nlist; 
 u_long  nz_size; 
#define nz_un  nlist.n_un 
#define nz_strx  nlist.n_un.n_strx 
#define nz_name  nlist.n_un.n_name 
#define nz_type  nlist.n_type 
#define nz_value nlist.n_value 
#define nz_desc  nlist.n_desc 
#define nz_other nlist.n_other 
};
nlist
( nlist(3) 参照)。
nz_size
このシンボルで表現されるデータのサイズ。

実行時のリンクエディットで行われるシンボル検索を高速化するため、共有オブジェクトのテキストセグメントにハッシュテーブルが含まれています。 section_dispatch_tablesdt_hash フィールドは、 rrs_hash 構造体を指し示します:

struct rrs_hash { 
 int rh_symbolnum;  /* シンボル番号 */ 
 int rh_next;  /* 次のハッシュエントリ */ 
};
rh_symbolnum
共有オブジェクトのシンボルテーブル ( ld_symbols で与えられます) での当該シンボルのインデックス。
rh_next
衝突が起きたとき、このフィールドは、このハッシュテーブルのバケットにおける次のエントリのオフセットを保持します。最終バケット要素の場合は、0 となります。

rt_symbol 構造体は、実行時にアロケートされるコモン(commons)と共有オブジェクトからコピーされるデータアイテムを追跡するのに用いられます。これらのアイテムは、リンクリストで管理され、デバッガでの利用のために so_debug 構造体 (後述) 中の dd_cc フィールドによって公開されます。

struct rt_symbol { 
 struct nzlist  *rt_sp; 
 struct rt_symbol *rt_next; 
 struct rt_symbol *rt_link; 
 caddr_t   rt_srcaddr; 
 struct so_map  *rt_smp; 
};
rt_sp
シンボル記述。
rt_next
次の rt_symbol の仮想アドレス。
rt_link
ハッシュバケットにおける次の要素。 ld.so の内部で用いられます。
rt_srcaddr
共有オブジェクト中での初期化済データのソース位置。
rt_smp
この実行時シンボルが記述するデータの元のソースとなる共有オブジェクト。

so_debug 構造体は、実行時リンクエディットの結果、当該プロセスのアドレス空間にロードされたあらゆる共有オブジェクトの情報を得るために、デバッガによって利用されます。実行時リンクエディタは、プロセスの初期化処理の一部として実行されるため、共有オブジェクトからシンボルにアクセスしようとするデバッガは、 crt0 からリンクエディタが呼ばれた後でのみそれが可能となります。ダイナミックリンクされているバイナリは、 so_debug 構造体を持っています。この構造体の場所は、 _dynamic 中の d_debug フィールドで指示されます。

struct  so_debug { 
 int dd_version; 
 int dd_in_debugger; 
 int dd_sym_loaded; 
 char    *dd_bpt_addr; 
 int dd_bpt_shadow; 
 struct rt_symbol *dd_cc; 
};
dd_version
このインタフェースのバージョン番号。
dd_in_debugger
当該プログラムがデバッガの制御下にあることを実行時リンカに知らせるためにデバッガによってセットされます。
dd_sym_loaded
共有オブジェクトをロードすることで実行時リンカがシンボルを追加した場合、実行時リンカによってセットされます。
dd_bpt_addr
デバッガに制御を移すために実行時リンカによってセットされるブレークポイントアドレス。このアドレスは、_main 呼び出しの前に、スタートアップモジュール crt0.o によってある適切な場所に決定されます。
dd_bpt_shadow
アドレス dd_bpt_addr にあった元の機械命令を保持します。デバッガは、プログラム実行を再開する前にこの機械命令を元に戻すことになっています。
dd_cc
デバッガが必要とする可能性のある、実行時にアロケートしたシンボルのリンクリストへのポインタ。

ld_entry 構造体は、 ld.so 中のサービスルーチン一式を定義します。

struct ld_entry { 
 void *(*dlopen)(char *, int); 
 int (*dlclose)(void *); 
 void *(*dlsym)(void *, char *); 
 char *(*dlerror)(void); 
};

crt_ldso 構造体は、crt0 中のスタートアップコードと ld.so との間のインタフェースを定義します。

struct crt_ldso { 
 int  crt_ba; 
 int  crt_dzfd; 
 int  crt_ldfd; 
 struct _dynamic *crt_dp; 
 char  **crt_ep; 
 caddr_t  crt_bp; 
 char  *crt_prog; 
 char  *crt_ldso; 
 struct ld_entry *crt_ldentry; 
}; 
#define CRT_VERSION_SUN  1 
#define CRT_VERSION_BSD_2 2 
#define CRT_VERSION_BSD_3 3 
#define CRT_VERSION_BSD_4 4
crt_ba
crt0 によって ld.so がロードされた仮想アドレス。
crt_dzfd
SunOS では、このフィールドは、“ /dev/zero”へのオープンされたファイル記述子を保持し、 0 クリアされたデマンドページを得ます。 FreeBSD では、このフィールドは、-1 を保持します。
crt_ldfd
ld.so をロードするために crt0 が用いる、オープンされたファイル記述子を保持します。
crt_dp
main の _dynamic 構造体へのポインタ。
crt_ep
環境文字列へのポインタ。
crt_bp
メインプログラムがデバッガで実行される場合、実行時リンカがブレークポイントを置くアドレス。 so_debug を参照してください。
crt_prog
crt0 で決定されるメインプログラムの名前 (CRT_VERSION_BSD3 のみ)。
crt_ldso
crt0 でマップされる実行時リンカのパス (CRT_VERSION_BSD4 のみ)。

hints_header 構造体及び hints_bucket 構造体は、通常“ /var/run/ld.so.hints”に置かれるライブラリヒントのレイアウトを定義します。ライブラリヒントは、ファイルシステム中で共有オブジェクトイメージの在処をすばやく見つけるために ld.so によって利用されます。ヒントファイルの構成は、“a.out”とそれほど異なりません。つまりヒントファイルは、固定長ハッシュバケットのオフセットとサイズを決定するためのヘッダと、共通の文字列プールを持っています。

struct hints_header { 
 long  hh_magic; 
#define HH_MAGIC 011421044151 
 long  hh_version; 
#define LD_HINTS_VERSION_1 1 
 long  hh_hashtab; 
 long  hh_nbucket; 
 long  hh_strtab; 
 long  hh_strtab_sz; 
 long  hh_ehints; 
};
hh_magic
ヒントファイルのマジックナンバ。
hh_version
インタフェースのバージョン番号。
hh_hashtab
ハッシュテーブルのオフセット。
hh_strtab
文字列テーブルのオフセット。
hh_strtab_sz
文字列テーブルのサイズ。
hh_ehints
ヒントファイルで利用可能な最大オフセット。

/* 
 * ヒントファイルのハッシュテーブル要素 
 */ 
struct hints_bucket { 
 int  hi_namex; 
 int  hi_pathx; 
 int  hi_dewey[MAXDEWEY]; 
 int  hi_ndewey; 
#define hi_major hi_dewey[0] 
#define hi_minor hi_dewey[1] 
 int  hi_next; 
};
hi_namex
ライブラリを指定する文字列のインデックス。
hi_pathx
ライブラリのフルパス名を表す文字列のインデックス。
hi_dewey
共通ライブラリのバージョン番号。
hi_ndewey
hi_dewey 中の有効エントリ数。
hi_next
ハッシュ衝突の際の次のバケット。

警告

現在のところ、共有ライブラリ生成をサポートしているのは、(GNU) C コンパイラのみです。他のプログラミング言語では、利用できません。
October 23, 1993 FreeBSD