LINK(5) | FreeBSD File Formats Manual | LINK(5) |
名称
link — ダイナミックローダとリンクエディタインタフェース解説
インクルードファイル < 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_table の sdt_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_table の sdt_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 |