[ < ] [ > ]   [ << ] [ Up ] [ >> ]         [Top] [Contents] [Index] [ ? ]

17.16.5 初期化関数の扱われ方

ある種の言語では、コンパイルされたコードには コンストラクタ が含まれる。コンストラクタは、初期化ルーチンとも呼ばれることもあり、 プログラムの起動時にそのプログラムのデータを初期化する関数である。 この関数は、プログラムが「開始」するまえに呼ばれる必要がある。 つまり、main の前に呼ばれる必要がある。

いくつかの言語では、コンパイルを行なうと、 終了ルーチンとも呼ばれる デストラクタを生成する。 デストラクタは、プログラムが終了するときに呼び出されなくてはならない。

初期化と終了関数を動作させるためには、コンパイラがアセンブラコードに 何らかの出力を行なって、これらの関数が適切な時期に呼び出されるように しなければならない。

GCC 現在サポートしている、初期化関数と終了関数の実行方法には 大きく分けて二つの方法がある。 どちらの方法も、それぞれさらに二種類に分けられる。 構成方法のほとんどは、この4つの方法全部に共通している。

リンカは、これらの関数のリストを二つ構築しなければならない。 一つは、初期化関数のリストであり、__CTOR_LIST__ と呼ばれる。 もう一つは、終了関数のリストであり、__DTOR_LIST__ と呼ばれる。

どちらのリストも無視される関数ポインタが先頭に置かれる。 (この関数ポインタには、環境によって、0、1、あるいは後ろに置かれる関数 ポインタの数のどれかの値が置かれる。) この後に、0個以上のコンストラクタ(あるいはデストラクタ)への関数ポインタ の並びが続く。最後に、値が 0 の関数ポインタが置かれる。

オペレーティング・システムや実行形式ファイル形式に依存して、 ‘crtstuff.c’ か ‘libgcc2.c’ のどちらかが、起動時と終了時に これらのリストを走査する。 コンストラクタは、リスト中の順番とは逆の順序で呼び出される。 デストラクタは、リストの順番で呼び出される。

静的なコンストラクタを扱う最適な方法は、セクションに任意の名前を付ける ことができるオブジェクトファイル形式でしか使えない。 あるセクションがコンストラクタのリストのために取られる。 そして、もう一つ別のセクションがデストラクタのために取られる。 伝統的に、この二つのセクションは、‘.ctors’ と ‘.dtors’ と 呼ばれる。初期化関数を定義しているオブジェクトファイルは、 その関数を指すワードをコンストラクタセクションに置く。 リンカがこれらのワードをまとめ上げ、一つの連続した ‘.ctors’ セクション を作る。終了関数も同様に扱われる。

この方法を使うには、マクロ ASM_OUTPUT_CONSTRUCTORASM_OUTPUT_DESTRUCTOR に適切な定義を与える必要がある。 それには、普通は ‘svr4.h’ をインクルードすれば良い。

任意のセクションを使えるなら、さらに ‘crtstuff.c’ 中の コードの呼ばれ方により、二つの変種がある。 プログラムの起動時に実行される 初期化 セクションをサポートしている システムでは、‘crtstuff.c’ の中身がコンパイルされて、 そのセクションの中に入る。 プログラムは、gcc ドライバにより以下のようにリンクされる。

 
ld -o output_file crtbegin.o … crtend.o -lgcc

ある関数(__do_global_ctors) の先頭が、 ‘crtbegin.o’ の初期化セクションに現れる。 この関数の残りの部分は ‘crtend.o’ の初期化セクションに現れる。 リンカは、初期化セクションのこの二つの部分を合わせて、関数全体を 作る。この中間にリンクされるユーザのオブジェクトファイルが どれもコードに寄与するなら、そのコードは __do_global_ctors の 本体の一部として実行される。

この方法を使うには、マクロ INIT_SECTION_ASM_OP を適切に 定義しなくてはならない。

初期化セクションが使えないときは、INIT_SECTION_ASM_OP は 定義しないこと。 そうすると、__do_global_ctors は他の全ての関数と同様、 テキストセクションの中に作られ、‘libgcc.a’ に置かれる。 GCC が main という名前の関数をコンパイルするときは、 その関数のプロローグコードの直後の最初の実行可能なコードとして、 __main を呼び出す手続きを挿入する。 関数 __main は、‘libgcc2.c’ で定義されており、 単に ‘__do_global_ctors’ を呼び出す。

ファイル形式が任意個数のセクションをサポートしていない場合は、 また二つの変種がある。一番簡単な方法の場合は、GNU リンカ(GNU ld) と a.out 形式を使わなければならない。 この場合、ASM_OUTPUT_CONSTRUCTOR を定義して、 ‘N_SETT’ 型の .stabs エントリを一個作るようにする。 この ‘N_SETT’ 型のエントリは、__CTOR_LIST__ という名前を 参照しており、初期化コードを含む void 型の関数のアドレスを、その 値として持っている。 GNU リンカはこれを、その値をある「集合」に追加する要求として 認識する。この値は累積され、最終的には上記の形式のベクトルとして 実行形式ファイルに置かれる。 このベクトルの前には個数(無視される)が置かれ、ベクトルの最後の要素は ゼロとなる。 ASM_OUTPUT_DESTRUCTOR も同様に扱われる。 初期化セクションが使えないので、INIT_SECTION_ASM_OP が ないと、main は上記のように __main を呼び出すように コンパイルされ、初期化処理を開始する。

最後の変種では、任意個数のセクションも GNU リンカも使わない。 ダイナミックリンクを行ないたい場合と ECOFF のように、GNU リンカがサポート していないファイル形式を使うときには、これが好ましい方法である。 この場合、ASM_OUTPUT_CONSTRUCTORN_SETT を 作り出さない。初期化関数と終了関数は単にその名前により認識される。 このため、リンクの段階で、collect2 という余分のプログラムを 必要とする。このプログラムは、GNU CC と合わせて使うときは、リンカ のふりをする。 collect2 は、通常のリンカを実行させることで自分の仕事を 行なう。ただし、初期化関数と終了関数のベクトルを含むよう調整も 行なう。 これらの関数は上記のように __main を経由して呼び出される。

これらのコンフィギュレーションの選択肢から選び出す作業は、 オペレーティングシステム依存のファイルを ‘config’ サブディレクトリに 置くことで簡素化される。 ここに置くファイルで関係するパラメータを全て定義する。 普通は、そのうちの一個のファイルを読者の固有の 機種依存のコンフィギュレーションファイルに含めれば充分である。 オペレーティング依存のファイルには以下のものがある。

aoutos.h

`a.out' 形式を使用するオペレーティングシステム向けのファイル。

next.h

`MachO' 形式を使用するオペレーティングシステム向けのファイル。

svr3.h

System V Release 3、それに `COFF' 形式を使用する類似のシステム向けのファイル。

svr4.h

System V Release 4、それに `ELF 形式を使用する類似のシステム向けのファイル。

vms.h

VMS システム向けのファイル。


This document was generated using texi2html 1.78.