6.6 .tlsセクション

.tlsセクションは、静的スレッド ローカル ストレージ(TLS)のための直接PE/COFFサポートを提供します。TLSはWindows NTによってサポートされている特殊ストレージ クラスで、ここではデータオブジェクトは自動(スタック)変数ではなく、コードを実行する個別スレッドにローカルなものとなります。したがって、各スレッドはTLSを使って宣言された変数で別々の値を維持することができます。

APIコール TlsAllocTlsFreeTlsSetValue、および TlsGetValueを使うことによって、任意の量のTLSデータをサポートすることができます。PE/COMMの実装は、このAPIを使うためのもう1つのアプローチで、高水準言語のプログラマの立場からは、このほうが単純だという利点があります。この実装により、TLSデータの定義と初期化を、プログラム内での通常の静的変数と同様の方法で行うことができます。たとえば、Microsoft Visual C++では。静的TLS変数をWindows APIを使わずに、次ように定義できます。

__declspec (thread) int tlsFlag = 1;
このプログラミング構成をサポートするために、PE/COFFの.tlsセクションは、次の情報をしてします。初期化データ、スレッドごとの初期化と終了を行うコールバック ルーチン、およびこの後で説明するTLSインデックス。

注意:
静的に宣言されたTLSデータオブジェクトは、静的にロードされるイメージ ファイル内でのみ使用可能です。この事実は、DLL内で静的TLSデータを使うことは、そのDLLを熟知しているか、あるいは何かが静的にリンクされているのでない限りあまり信用できず、API関数LoadLibraryによって動的にロードされることはあり得ません。

実行コードは、次の手順を通じて、静的TLSデータ オブジェクトにアクセスします。

  1. リンク時に、リンカはTLSディレクトリのAddress of Indexフィールドをセットします。このフィールドはプログラムがどこでTLSインデックスを受け取ることを期待しているかを示します。

  2. .TLSディレクトリのメモリイメージを定義し、それに特殊な名前"__tls_used"(Intel x86プラットフォーム)または"_tls_used"(他のプラットフォーム)を与えることによって、Microsoft実行時ライブラリはこの処理を行います。リンカはこのメモリイメージを探し、そこにあるデータを使ってTLSディレクトリを作成します。TLSをサポートし、Microsoftリンカを使う、他のコンパイラは、この同じテクニックを使わなければなりません。

  3. スレッドが作成されると、ローダは、FSレジスタ内にスレッド環境ブロック(TEB)のアドレスを置くことによって、スレッドのTLS配列のアドレスをやり取りします。TLS配列へのポインタは、TEBの先頭から0x2Cのオフセットにあります。この動作はIntel x86に固有のものです。

  4. ローダはTLSインデックスの値をAddress of Indexフィールドによって示された場所に割り当てます。

  5. 実行コードは、TLSインデックスおよびTLS配列の場所を取り出します。

  6. コードは、TLSインデックスおよびTLS配列の位置(インデックスを4倍し、配列のオフセットとして使用します)を使用して与えられたプログラムおよびモジュールのためのTLSデータ領域のアドレスを取得します。各スレッドには専用のTLSデータ領域がありますが、これはプログラムに対して透過的であり、個々のスレッドに対してどのようにデータが割り当てられるかを知る必要はありません。

  7. 個々のTLSデータ オブジェクトは、TLSデータ領域に対する何らかの固定オフセットとしてアクセスされます。TLS配列はシステムが各スレッドのために維持するアドレスの配列です。この配列内の各アドレスは、プログラム内の与えられたモジュール(.EXEまたはDLL)のTLSデータの位置を示します。TLSインデックスは配列のどのメンバを使うかを示します(インデックスはモジュールを示す番号で、そのシステムにとってだけ意味を持ちます)。


戻る