コンパイル環境は、スレッド固有データの宣言をサポートします。このデータは、スレッド特有データやスレッド専用データと呼ばれることもありますが、一般には頭文字で TLS と呼ばれます。変数をスレッド固有として宣言すると、コンパイラは自動的にこれらの変数をスレッド単位で割り当てます。
この機能の組み込みサポートには、次に示す 3 つの目的があります。
スレッド固有のデータを割り当てる POSIX インタフェースの構築基盤を提供する。
アプリケーションとライブラリでスレッド固有変数を直接使用するための、便利で効果的なメカニズムを提供する。
コンパイラが、ループ並列化による最適化をする場合に、TLS を必要なだけ割り当てることができる。
次の例に示すように、__thread キーワードを使用すると、変数をスレッド固有として宣言できます。
__thread int i; __thread char *p; __thread struct state s;
ループの最適化の際に、コンパイラは必要に応じてスレッド固有一時領域を作成することがあります。
__thread キーワードは任意の大域変数、ファイルスコープの静的変数、または関数スコープの静的変数に適用できます。常にスレッド固有である自動変数には影響を与えません。
C++ では、初期化に静的なコンストラクタが必要となる場合には、スレッド固有変数の初期化が行われないことがあります。静的なコンストラクタを必要としないかぎり、スレッド固有変数は通常の静的変数に有効な任意の値に初期化できます。
変数は、(スレッド固有であるかどうかにかかわらず) スレッド固有変数のアドレスに静的に初期化することはできません。
スレッド固有変数の宣言と参照は外部的に行えます。スレッド固有変数は、通常のシンボルと同じ割り込み規則に従う必要があります。
さまざまな TLS アクセスモデルを利用できます。「スレッド固有領域のアクセスモデル」を参照してください。共有オブジェクトを開発するときは、オブジェクトの読み込みに関連して一部のアクセスモデルに適用される制限に注意するようにしてください。共有オブジェクトは、プロセスの起動時に動的に読み込むことができ、またプロセスの起動後には、遅延読み込み、フィルタ、または dlopen(3C) によって、動的に読み込むことができます。プロセスの起動が完了すると、メインスレッドのスレッドポインタが確立されます。すべての静的な TLS 領域要件は、スレッドポインタが確立される前に計算されます。
スレッド固有変数を参照する共有オブジェクトでは、その参照を含むすべての変換ユニットは、動的な TLS モデルを使ってコンパイルするようにしてください。このアクセスモデルを使用すると、共有オブジェクトをもっとも柔軟に読み込むことができます。ただし、静的な TLS モデルを使用すると、コードの速度が向上します。静的な TLS モデルを使用する共有オブジェクトは、プロセスを初期化するときに読み込むことができます。ただし、プロセスを初期化したあとは、静的な TLS モデルを使用する共有オブジェクトの読み込みは、十分なバックアップ TLS 記憶域が使用可能な場合にのみ実行できます。「プログラムの起動」を参照してください。
スレッド固有変数には、アドレス演算子 & を使用できます。この演算子は、実行時に評価されて、現在のスレッド内の変数のアドレスを返します。この演算子によって取得されたアドレスは、アドレスを評価したスレッドが存在するかぎり、プロセス内のあらゆるスレッドで自由に使用できます。スレッドが終了した時点で、そのスレッド内のスレッド固有変数を指すポインタはすべて無効になります。
スレッド固有変数のアドレスを取得するために dlsym(3C) を使用すると、dlsym() を呼び出したスレッド内におけるその変数のインスタンスのアドレスが返されます。
コンパイル時に割り当てられたスレッド固有データのコピーは、実行される個々のスレッドに、個別に関連付けられる必要があります。このデータを提供するために、TLS セクションを使用してサイズと初期の内容を指定します。コンパイル環境は、SHF_TLS フラグで識別されるセクション内に TLS を割り当てます。これらのセクションは、領域がどのように宣言されているかにもとづき、初期化された TLS と初期化されていない TLS を提供します。
初期化されたスレッド固有変数は、.tdata セクション内または .tdata1 セクション内に割り当てます。この初期化は再配置を必要とする場合があります。
初期化されていないスレッド固有変数は、COMMON シンボルとして定義します。その結果の割り当ては、.tbss セクション内で行われます。
初期化されていないセクションは、適切な整列になるようにパッドを入れられて、初期化されたセクションの直後に割り当てられます。結合されたこれらのセクションは、新しいスレッドの作成時に TLS の割り当てに使用できる TLS テンプレートとなります。このテンプレートの初期化された部分を、TLS 初期化イメージと呼びます。初期化されたスレッド固有変数の結果として発生する再配置はすべて、このテンプレートに適用されます。その後、新しいスレッドが初期値を要求すると、再配置された値が使用されます。
TLS シンボルのシンボルタイプは STT_TLS です。これらのシンボルには、TLS テンプレートの先頭からの相対オフセットが割り当てられます。これらのシンボルに関連付けられた実際の仮想アドレスとは無関係です。このアドレスが指すのはテンプレートだけで、各データ項目のスレッドごとのコピーではありません。動的実行可能ファイルと共有オブジェクトでは、STT_TLS シンボルの st_value フィールドに、定義済みシンボルの場合は割り当てられた TLS オフセットが含まれます。未定義シンボルの場合は、このフィールドにゼロが含まれます。
TLS へのアクセスをサポートするために、再配置がいくつか定義されます。「SPARC: スレッド固有領域の再配置のタイプ」、「32 ビット x86: スレッド固有領域の再配置のタイプ」、および「x64: スレッド固有領域の再配置のタイプ」を参照してください。TLS 再配置は通常、タイプ STT_TLS のシンボルを参照します。TLS 再配置は、GOT エントリに関連するローカルセクションシンボルも参照できます。この場合、割り当てられた TLS のオフセットが、関連する GOT エントリに保存されます。
動的実行可能ファイルと共有オブジェクトでは、PT_TLS プログラムエントリが TLS テンプレートを記述します。このテンプレートには、次のメンバーが含まれます。
表 8–1 ELF PT_TLS プログラムヘッダーエントリ
メンバー |
値 |
---|---|
p_offset |
TLS 初期化イメージのファイルオフセット |
p_vaddr |
TLS 初期化イメージの仮想メモリーアドレス |
p_paddr |
0 |
p_filesz |
TLS 初期化イメージのサイズ |
p_memsz |
TLS テンプレートの合計サイズ |
p_flags |
PF_R |
p_align |
TLS テンプレートの整列 |
TLS は、プログラムの存続中に、次の 3 つの機会に作成されます。
プログラムの起動時。
新しいスレッドの作成時。
プログラムの起動に続いて共有オブジェクトが読み込まれたあと、スレッドがはじめて TLS ブロックを参照するとき。
スレッド固有データ領域は、図 8–1 に示すように実行時に配置されます。
プログラムの起動時に、実行システムはメインスレッド用の TLS を作成します。
まず実行時リンカーが、読み込まれたすべての動的オブジェクト (動的な実行可能ファイルを含む) の TLS テンプレートを論理的に結合し、単一の静的なテンプレートとしてまとめます。各動的オブジェクトの TLS テンプレートには、結合されたテンプレート内のオフセット tlsoffsetm が次のように割り当てられます。
tlsoffset1 = round(tlssize1, align1 )
tlsoffsetm+1 = round(tlsoffsetm + tlssizem+1, alignm+1)
tlssizem+1 は動的オブジェクト m の割り当てテンプレートのサイズで、 alignm+1 は整列です。ここで、1 <= m <= M であり、M は読み込まれる動的オブジェクトの合計数です。round(offset, align) 関数は、align の次の倍数に丸められたオフセットを返します。
次に、実行時リンカーは、起動時の TLS に必要な割り当てサイズの tlssizeS を計算します。このサイズは、tlsoffset M に 512 バイトを加えた値に等しくなります。この加算により、静的な TLS 参照のバックアップ予約が得られます。静的な TLS 参照を作成し、プロセスの初期化後に読み込まれる共有オブジェクトは、このバックアップ予約に割り当てられます。ただし、この予約のサイズは固定および限定されています。また、この予約が対応しているのは、初期化されていない TLS データ項目用の記憶域の提供だけです。柔軟性を最大限高めるため、動的な TLS モデルを使用して、共有オブジェクトがスレッドローカル変数を参照するようにしてください。
計算された TLS サイズ tlssizeS に関連付けられる静的な TLS 領域は、スレッドポインタ tpt の直前に配置されます。TLS データに対するアクセスは、tpt からの減算にもとづいて行われます。
静的な TLS 領域は、初期化レコードのリンクリストに関連付けられます。このリスト内の各レコードは、読み込まれた動的オブジェクトごとにその TLS 初期化イメージを記述するものです。各レコードには、次のフィールドが含まれています。
TLS 初期化イメージを指すポインタ。
TLS 初期化イメージのサイズ。
オブジェクトの tlsoffsetm。
オブジェクトが静的な TLS モデルを使用するかどうかを示すフラグ。
スレッドライブラリは、この情報を使用して初期スレッドに領域を割り当てます。この領域が初期化され、初期スレッド用に動的な TLS ベクトルが作成されます。
初期スレッドと、新しく作成されるスレッドに対して、スレッドライブラリは読み込まれる動的オブジェクトごとに新しい TLS ブロックを割り当てます。ブロックは、個別に割り当てられることも、単一の連続ブロックとして割り当てられることもあります。
各スレッド t は関連するスレッドポインタ tpt を持ち、このポインタはスレッド制御ブロック TCB を指します。スレッドポインタ tp には、常に、現在動作しているスレッドの tpt の値が含まれます。
続いてスレッドライブラリは、現在のスレッド t のためにポインタのベクトル dtvt を作成します。各ベクトルの最初の要素には、ベクトルを拡張すべきタイミングを決定するために使用される生成番号 gent が入ります。「スレッド固有領域ブロックの遅延割り当て」を参照してください。
ベクトル内の残りの各要素 dtvt,m は、動的オブジェクト m に属する TLS 用に予約されたブロックへのポインタです。
起動後動的に読み込まれたオブジェクトについては、スレッドライブラリは TLS ブロックの割り当てを延期します。割り当ては、読み込まれたオブジェクト内で TLS 変数に対して最初の参照が行われる時に発生します。割り当てが延期されたブロックの場合、ポインタ dtvt,m は実装が定める特別な値に設定されます。
実行時リンカーは、ベクトル内の単一の要素 dtvt,1 を共有するために、すべての起動オブジェクトの TLS テンプレートをグループ化できます。このグループ化によって、前述のオフセット計算や初期化レコードのリストの作成が影響を受けることはありません。しかし、次の節では、M の値 (オブジェクトの合計数) は値 1 から始まっています。
続いて、スレッドライブラリが、新しい領域ブロック内の対応する場所に初期化イメージをコピーします。
動的な TLS だけが含まれる共有オブジェクトは、プロセスの起動に続いて無制限に読み込むことができます。実行時リンカーは、初期化レコードのリストを拡張して新しいオブジェクトの初期化テンプレートを含めます。新しいオブジェクトには、インデックス m = M + 1 が与えられます。カウンタ M は 1 ずつ増えていきます。しかし、新しい TLS ブロックの割り当ては、それらが実際に参照されるまで延期されます。
動的な TLS だけが含まれる共有オブジェクトの読み込みが解除されると、その共有オブジェクトが使用している TLS ブロックは解放されます。
静的な TLS が含まれる共有オブジェクトは、プロセスの起動に続いて一定の制限内で読み込むことができます。静的な TLS 参照を満たすことができるのは、残りのバックアップ TLS 予約からだけです。「プログラムの起動」を参照してください。この予約のサイズは制限されています。また、この予約で提供できるのは、初期化されていない TLS データ項目用の記憶域だけです。
静的な TLS を含む共有オブジェクトは、読み込み解除されません。静的な TLS 処理の結果として、共有オブジェクトには削除不可のタグが付けられます。
動的な TLS モデルでは、スレッド t がオブジェクト m の TLS ブロックにアクセスする必要が生じた場合、コードは dtv t を更新し、TLS ブロックの初期割り当てを行います。スレッドライブラリは、動的な TLS 割り当てが行えるように、次のインタフェースを提供します。
typedef struct { unsigned long ti_moduleid; unsigned long ti_tlsoffset; } TLS_index; extern void * __tls_get_addr(TLS_index * ti); (SPARC and x64) extern void * ___tls_get_addr(TLS_index * ti); (32–bit x86)
この関数の SPARC 定義と 64ビットの x86 定義は、同一の関数シグニチャーを持ちます。しかし 32 ビットの x86 バージョンは、スタック上で引数を渡すデフォルトの呼び出し規約を使用しません。代わりに 32 ビットの x86 バージョンは、より効率の良い %eax レジスタによって引数を渡します。この代替呼び出し手法を使用することを示すため、32 ビットの x86 関数名にはその先頭に 3 つの下線が付いています。
tls_get_addr() の両バージョンとも、スレッドごとの生成カウンタ gent を調べ、ベクトルが更新を必要としていないかを確認します。ベクトル dtvt が古い場合、ルーチンがベクトルを更新し、必要に応じ、追加エントリ用のスペースを確保するため再割り当てを行います。続いてこのルーチンは、dtvt,m に対応する TLS ブロックがすでに割り当てられているかを調べます。ベクトルが割り当てられていない場合、このルーチンはブロックの割 り当てと初期化を行います。このルーチンは、実行時リンカーが提供する初期化レコードリスト内の情報を使用します。ポインタ dtv t,m は、割り当てられたブロックを指すように設定されます。ルーチンは、ブロック内の指定されたオフセットへのポインタを返します。
各 TLS 参照は、次のアクセスモデルのどれかになります。ここでは、もっとも一般的なモデル (しかし最適化の程度はもっとも低い) から順に、もっとも高速なモデル (しかし制限度は高い) へと並んでいます。
このモデルでは、共有オブジェクトまたは動的実行可能ファイルから、すべての TLS 変数を参照できます。このモデルでは、TLS ブロックが特定のスレッドからはじめて参照される時まで、このブロックの割り当てを延期することもできます。
このモデルは、GD モデルを最適化したものです。コンパイラが、構築されるオブジェクト内で変数がローカルに結合されているか、あるいは保護されていると判断することがあります。この場合、コンパイラは、動的な tlsoffset を静的に結合してこのモデルを使用するように、リンカーに指示します。このモデルにより、GD モデルを上回る性能が得られます。dtv()0,m のアドレスの確認は、関数ごとに tls_get_addr を 1 度呼び出すだけです。リンク編集時に結合される動的な TLS オフセットは、参照ごとに dtv0,m アドレスに追加されます。
このモデルは、初期の静的な TLS テンプレートの一部として利用できる TLS 変数だけを参照できます。このテンプレートは、プロセスの起動時に使用できるすべての TLS ブロック、および小規模なバックアップ予約で構成されます。「プログラムの起動」を参照してください。このモデルでは、変数 x の、スレッドポインタからの相対オフセットは、x の GOT エントリ内に保存されます。
このモデルでは、初期プロセスの起動後に、遅延読み込み、フィルタ、dlopen(3C) などによって読み込まれる共有ライブラリから、限定された数の TLS 変数を参照できます。このアクセスは、固定のバックアップ予約から満たされます。この予約で提供できるのは、初期化されていない TLS データ項目用の記憶域だけです。柔軟性を最大限高めるため、動的な TLS モデルを使用して、共有オブジェクトがスレッドローカル変数を参照するようにしてください。
フィルタを使用して、静的な TLS の使用を動的に選択できます。共有オブジェクトを、動的な TLS を使用するように構築します。さらに共有オブジェクトを、静的な TLS を使用するために構築された対応物に対する補助フィルタとして動作するよう、構築することができます。静的な TLS オブジェクトの読み込みがリソースにより許可される場合に、そのオブジェクトが使用されます。それ以外の場合、動的な TLS オブジェクトへのフォールバックにより、共有オブジェクトの提供する機能が常に使用可能であることが保証されます。フィルタの詳細については、「フィルタとしての共有オブジェクト」を参照してください。
このモデルは、動的実行可能ファイルの TLS ブロックの一部である TLS 変数だけを参照できます。リンカーは、動的な再配置や GOT の参照を別途行うことなく、スレッドポインタからの相対オフセットを静的に計算します。このモデルを使用して動的実行可能ファイルの外部に存在する変数を参照することはできません。
リンカーは、妥当と判断する場合は、比較的一般的なアクセスモデルから、より最適化されたモデルへとコードを移行できます。この移行は、独特な TLS 再配置を使用することで行えます。これらの再配置は、更新を要求するだけでなく、どの TLS アクセスモデルが使用されているかの特定もします。
作成されるオブジェクトのタイプとともに TLS アクセスモデルを認識することで、リンカーは変換を実行できます。たとえば、GD アクセスモデルを使用している再配置可能オブジェクトが、動的実行可能ファイルにリンクされているとします。この場合、リンカーは IE アクセスモ デルまたは LE アクセスモデルを使用して参照を適宜移行できます。そのあとで、そのモデルに必要な再配置が行われます。
次の図は、それぞれのアクセスモデルと、あるモデルから別のモデルにどのように移行するかを示しています。
SPARC では、スレッド固有変数へのアクセスに次のコードシーケンスモデルを使用できます。
このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている GD モデルを実装します。
表 8–2 SPARC: General Dynamic スレッド固有変数のアクセスコード
sethi 命令は R_SPARC_TLS_GD_HI22 再配置を生成し、add 命令は R_SPARC_TLS_GD_LO10 再配置を生成します。これらの再配置は、変数 x の TLS_index 構造体を保持する領域を GOT 内に割り当てるように、リンカーに指示します。リンカーは、この新しい GOT エントリに GOT からの相対オフセットを代入することによって、この再配置を処理します。
読み込みオブジェクトインデックスと x の TLS ブロックインデックスは実行時まで不明です。したがって、リンカーは、実行時リンカーによって処理されるように、GOT に対する R_SPARC_TLS_DTPMOD32 再配置と R_SPARC_TLS_DPTOFF32 再配置を設定します。
2 番目の add 命令は、R_SPARC_TLS_GD_ADD 再配置を生成します。この再配置が使用されるのは、リンカーによって GD コードシーケンスがほかのシーケンスに変更される場合だけです。
call 命令は特別な構文である x@TLSPLT を使用します。この call 命令は TLS 変数を参照し、R_SPARC_TLS_GD_CALL 再配置を生成します。この再配置は、__tls_get_addr() 関数の呼び出しを結合するようリンカーに指示し、call 命令を GD コードシーケンスに関連付けます。
add 命令は、call 命令の前に指定する必要があります。add 命令を、呼び出しの遅延スロットに配置することはできません。これは、あとで発生するコード変換が既知の順序を必要とするためです。
R_SPARC_TLS_GD_ADD 再配置によってタグが付けられた add 命令の GOT ポインタとして使用されるレジスタは、add 命令内の最初のレジスタでなければなりません。このように指定することで、リンカーはコード変換時に GOT ポインタであるレジスタを識別できるようになります。
このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている LD モデルを実装します。
表 8–3 SPARC: Local Dynamic スレッド固有変数のアクセスコード
最初の sethi 命令は R_SPARC_TLS_LDM_HI22 再配置を生成し、add 命令は R_SPARC_TLS_LDM_LO10 再配置を生成します。これらの再配置は、現在のオブジェクトの TLS_index 構造体を保持する領域を GOT に割り当てるように、リンカーに指示します。リンカーは、この新しい GOT エントリに GOT からの相対オフセットを代入することによって、この再配置を処理します。
読み込みオブジェクトインデックスは実行時まで不明です。したがって、R_SPARC_TLS_DTPMOD32 再配置が作成され、TLS_index 構造体の ti_tlsoffset フィールドにゼロが埋め込まれます。
2 つめの add 命令には R_SPARC_TLS_LDM_ADD 再配置によってタグが付けられ、call 命令には R_SPARC_TLS_LDM_CALL 再配置によってタグが付けられます。
以降の sethi 命令は R_SPARC_LDO_HIX22 再配置を生成し、xor 命令は R_SPARC_TLS_LDO_LOX10 再配置を生成します。各局所シンボルの TLS オフセットはリンク編集時に認識されるため、これらの値は直接埋め込まれます。add 命令には、R_SPARC_TLS_LDO_ADD 再配置によってタグが付けられます。
手続きが複数の局所シンボルを参照する場合には、コンパイラは TLS ブロックの基底アドレスを取得するコードを 1 度だけ生成します。以後、各シンボルのアドレスの計算にはこの基底アドレスが使用され、個別にライブラリを呼び出すことはありません。
R_SPARC_TLS_LDO_ADD によってタグが付けられた add 命令内の TLS オブジェクトアドレスが入ったレジスタは、命令シーケンス内の最初のレジスタでなければなりません。このように指定することで、リンカーはコード変換時にレジスタを識別できるようになります。
このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている IE モデルを実装します。
表 8–4 32 ビット SPARC: Initial Executable スレッド固有変数のアクセスコード
sethi 命令は R_SPARC_TLS_IE_HI22 再配置を生成し、or 命令は R_SPARC_TLS_IE_LO10 再配置を生成します。これらの再配置は、シンボル x の静的な TLS オフセットを保存する領域を GOT 内に作成するように、リンカーに指示します。実行時リンカーがシンボル x の負の静的 TLS オフセットを埋め込むよう、GOT に対する R_SPARC_TLS_TPOFF32 の再配置は、未処理の状態に置かれます。ld 命令には R_SPARC_TLS_IE_LD 再配置によってタグが付けられ、add 命令には R_SPARC_TLS_IE_ADD 再配置によってタグが付けられます。
R_SPARC_TLS_IE_ADD 再配置によってタグが付けられた add 命令の GOT ポインタとして使用されるレジスタは、この命令内の最初のレジスタでなければなりません。このように指定することで、リンカーはコード変換時に GOT ポインタであるレジスタを識別できるようになります。
このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている IE モデルを実装します。
表 8–5 64 ビット SPARC: Initial Executable スレッド固有変数のアクセスコード
このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている LE モデルを実装します。
表 8–6 SPARC: Local Executable スレッド固有変数のアクセスコード
sethi 命令は R_SPARC_TLS_LE_HIX22 再配置を生成し、xor 命令は R_SPARC_TLS_LE_LOX10 再配置を生成します。リンカーは、実行可能ファイルで定義されたシンボルの静的な TLS オフセットに、これらの再配置を直接結合します。実行時には、再配置処理は不要です。
次の表に、SPARC 用に定義された TLS 再配置を示します。表内の説明では、次の表記が使用されています。
TLS_index 構造体を保持するために、GOT 内に連続した 2 つのエントリを割り当てます。この情報は、__tls_get_addr() に渡されます。このエントリを参照する命令は、2 つの GOT エントリのうちの最初のエントリのアドレスに結合されます。
TLS_index 構造体を保持するために、GOT 内に連続した 2 つのエントリを割り当てます。この情報は、__tls_get_addr() に渡されます。この構造体の ti_tlsoffset フィールドは 0 に設定され、ti_moduleid は実行時に埋め込まれます。__tls_get_addr () 呼び出しは、動的な TLS ブロックの開始オフセットを返します。
TLS ブロックからの相対 tlsoffset を計算します。
静的な TLS ブロックからの負の相対 tlsoffset を計算します。この値は、TLS アドレスを計算するためにスレッドポインタに追加されます。
TLS シンボルを含むオブジェクトの識別子を計算します。
名前 |
値 |
フィールド |
計算 |
---|---|---|---|
R_SPARC_TLS_GD_HI22 |
56 |
T-simm22 |
@dtlndx(S + A) >> 10 |
R_SPARC_TLS_GD_LO10 |
57 |
T-simm13 |
@dtlndx(S + A) & 0x3ff |
R_SPARC_TLS_GD_ADD |
58 |
なし |
この表のあとの説明を参照してください。 |
R_SPARC_TLS_GD_CALL |
59 |
V-disp30 |
この表のあとの説明を参照してください。 |
R_SPARC_TLS_LDM_HI22 |
60 |
T-simm22 |
@tmndx(S + A) >> 10 |
R_SPARC_TLS_LDM_LO10 |
61 |
T-simm13 |
@tmndx(S + A) & 0x3ff |
R_SPARC_TLS_LDM_ADD |
62 |
なし |
この表のあとの説明を参照してください。 |
R_SPARC_TLS_LDM_CALL |
63 |
V-disp30 |
この表のあとの説明を参照してください。 |
R_SPARC_TLS_LDO_HIX22 |
64 |
T-simm22 |
@dtpoff(S + A) >> 10 |
R_SPARC_TLS_LDO_LOX10 |
65 |
T-simm13 |
@dtpoff(S + A) & 0x3ff |
R_SPARC_TLS_LDO_ADD |
66 |
なし |
この表のあとの説明を参照してください。 |
R_SPARC_TLS_IE_HI22 |
67 |
T-simm22 |
@got(@tpoff(S + A)) >> 10 |
R_SPARC_TLS_IE_LO10 |
68 |
T-simm13 |
@got(@tpoff(S + A)) & 0x3ff |
R_SPARC_TLS_IE_LD |
69 |
なし |
この表のあとの説明を参照してください。 |
R_SPARC_TLS_IE_LDX |
70 |
なし |
この表のあとの説明を参照してください。 |
R_SPARC_TLS_IE_ADD |
71 |
なし |
この表のあとの説明を参照してください。 |
R_SPARC_TLS_LE_HIX22 |
72 |
T-imm22 |
(@tpoff(S + A) ^0xffffffffffffffff) >> 10 |
R_SPARC_TLS_LE_LOX10 |
73 |
T-simm13 |
(@tpoff(S + A) & 0x3ff) | 0x1c00 |
R_SPARC_TLS_DTPMOD32 |
74 |
V-word32 |
@dtpmod(S + A) |
R_SPARC_TLS_DTPMOD64 |
75 |
V-word64 |
@dtpmod(S + A) |
R_SPARC_TLS_DTPOFF32 |
76 |
V-word32 |
@dtpoff(S + A) |
R_SPARC_TLS_DTPOFF64 |
77 |
V-word64 |
@dtpoff(S + A) |
R_SPARC_TLS_TPOFF32 |
78 |
V-word32 |
@tpoff(S + A) |
R_SPARC_TLS_TPOFF64 |
79 |
V-word64 |
@tpoff(S + A) |
いくつかの再配置型には、単純な計算を超えたセマンティクスが存在します。
この再配置は、GD コードシーケンスの add 命令にタグを付けます。GOT ポインタに使用されるレジスタは、シーケンス内の最初のレジスタです。この再配置によってタグが付けられる命令は、R_SPARC_TLS_GD_CALL 再配置によってタグが付けられる call 命令の前に置かれます。この再配置は、リンク編集時に TLS モデルを移行するために使用されます。
この再配置は、__tls_get_addr() 関数を参照する R_SPARC_WPLT30 再配置と同じように処理されます。この再配置は、 GD コードシーケンスの一部です。
この再配置は、LD コードシーケンスの最初の add 命令にタグを付けます。GOT ポインタに使用されるレジスタは、シーケンス内の最初のレジスタです。この再配置によってタグが付けられる命令は、R_SPARC_TLS_GD_CALL 再配置によってタグが付けられる call 命令の前に置かれます。この再配置は、リンク編集時に TLS モデルを移行するために使用されます。
この再配置は、__tls_get_addr() 関数を参照する R_SPARC_WPLT30 再配置と同じように処理されます。この再配置は、LD コードシーケンスの一部です。
この再配置は、LD コードシーケンス内の最後の add 命令にタグを付けます。コードシーケンスの先頭で計算されるオブジェクトアドレスを含むレジスタは、この命令における最初のレジスタです。この再配置により、リンカーはコード変換時にレジスタを識別できるようになります。
この再配置は、32 ビットの IE コードシーケンス内の ld 命令にタグを付けます。この再配置は、リンク編集時に TLS モデルを移行するために使用されます。
この再配置は、64 ビットの IE コードシーケンス内の ldx 命令にタグを付けます。この再配置は、リンク編集時に TLS モデルを移行するために使用されます。
この再配置は、IE コードシーケンス内の add 命令にタグを付けます。GOT ポインタに使用されるレジスタは、シーケンス内の最初のレジスタです。
x86 では、TLS へのアクセスに次のコードシーケンスモデルを使用できます。
このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている GD モデルを実装します。
表 8–8 32 ビット x86: General Dynamic スレッド固有変数のアクセスコード
leal 命令は R_386_TLS_GD 再配置を生成します。この再配置は、変数 x の TLS_index 構造体を保持する領域を GOT 内に割り当てるよう、リンカーに指示します。リンカーは、この新しい GOT エントリに GOT からの相対オフセットを代入することによって、この再配置を処理します。
読み込みオブジェクトインデックスと x の TLS ブロックインデックスは実行時まで不明なため、リンカーは、実行時リンカーによって処理されるように、GOT に対して R_386_TLS_DTPMOD32 再配置と R_386_TLS_DTPOFF32 再配置を設定します。生成された GOT エントリのアドレスは、___tls_get_addr() 呼び出しのためにレジスタ %eax に読み込まれます。
call 命令は、R_386_TLS_GD_PLT 再配置を生成します。この再配置は、___tls_get_addr () 関数の呼び出しを結合するようリンカーに指示し、call 命令を GD コードシーケンスに関連付けます。
call 命令は、leal 命令の直後に配置する必要があります。この要件は、コード変換を可能にするために必要です。
このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている LD モデルを実装します。
表 8–9 32 ビット x86: Local Dynamic スレッド固有変数のアクセスコード
最初の leal 命令は R_386_TLS_LDM 再配置を生成します。この再配置は、現在のオブジェクトの TLS_index 構造体を保持する領域を GOT に割り当てるように、リンカーに指示します。リンカーは、この新しいリンクテーブルエントリに GOT からの相対オフセットを代入することによって、この再配置を処理します。
読み込みオブジェクトインデックスは実行時まで不明です。したがって、R_386_TLS_DTPMOD32 再配置が作成され、構造体の ti_tlsoffset フィールドにゼロが埋め込まれます。call 命令には、R_386_TLS_LDM_PLT 再配置によってタグが付けられます。
各局所シンボルの TLS オフセットはリンク編集時に認識されるため、リンカーはこれらの値を直接埋め込みます。
手続きが複数の局所シンボルを参照する場合には、コンパイラは TLS ブロックの基底アドレスを取得するコードを 1 度だけ生成します。以後、各シンボルのアドレスの計算にはこの基底アドレスが使用され、個別にライブラリを呼び出すことはありません。
このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている IE モデルを実装します。
IE モデルには 2 つのコードシーケンスが存在します。その 1 つは、GOT ポインタを使用する、位置に依存しないコード用です。もう 1 つのシーケンスは、GOT ポインタを使用しない、位置に依存するコード用です。
表 8–10 32 ビット x86: 位置に依存しない Initial Executable スレッド固有変数のアクセスコード
コードシーケンス |
初期の再配置 |
シンボル |
0x00 movl %gs:0, %eax 0x06 addl x@gotntpoff(%ebx), %eax # %eax - contains address of TLS variable |
<none> R_386_TLS_GOTIE |
x |
未処理の再配置 |
シンボル |
|
GOT[n] |
R_386_TLS_TPOFF |
x |
addl 命令は R_386_TLS_GOTIE 再配置を生成します。この再配置は、シンボル x の静的な TLS オフセットを保存する領域を GOT 内に作成するように、リンカーに指示します。このとき、GOT テーブルに対する R_386_TLS_TPOFF 再配置は、未処理の状態に置かれます。あとで、実行時リンカーがシンボル x の静的な TLS オフセットを埋め込みます。
表 8–11 32 ビット x86: 位置に依存する Initial Executable スレッド固有変数のアクセスコード
コードシーケンス |
初期の再配置 |
シンボル |
0x00 movl %gs:0, %eax 0x06 addl x@indntpoff, %eax # %eax - contains address of TLS variable |
<none> R_386_TLS_IE |
x |
未処理の再配置 |
シンボル |
|
GOT[n] |
R_386_TLS_TPOFF |
x |
addl 命令は R_386_TLS_IE 再配置を生成します。この再配置は、シンボル x の静的な TLS オフセットを保存する領域を GOT 内に作成するように、リンカーに指示します。このシーケンスと位置に依存しない形式との主な違いは、GOT ポインタレジスタのオフセットを使用せず、作成される GOT エントリに直接、命令が結合されることです。このとき、GOT に対する R_386_TLS_TPOFF 再配置は、未処理の状態に置かれます。あとで、実行時リンカーがシンボル x の静的な TLS オフセットを埋め込みます。
次の 2 つのシーケンスに示すように、メモリー参照にオフセットを直接埋め込むことによって、変数 x の (アドレスではなく) 内容を読み込むことができます。
表 8–12 32 ビット x86: 位置に依存しない Initial Executable 動的スレッド固有変数のアクセスコード
コードシーケンス |
初期の再配置 |
シンボル |
0x00 movl x@gotntpoff(%ebx), %eax 0x06 movl %gs:(%eax), %eax # %eax - contains address of TLS variable |
R_386_TLS_GOTIE <none> |
x |
未処理の再配置 |
シンボル |
|
GOT[n] |
R_386_TLS_TPOFF |
x |
表 8–13 32 ビット x86: 位置に依存しない Initial Executable スレッド固有変数のアクセスコード
コードシーケンス |
初期の再配置 |
シンボル |
0x00 movl x@indntpoff, %ecx 0x06 movl %gs:(%ecx), %eax # %eax - contains address of TLS variable |
R_386_TLS_IE <none> |
x |
未処理の再配置 |
シンボル |
|
GOT[n] |
R_386_TLS_TPOFF |
x |
最後のシーケンスで、%ecx レジスタではなく %eax レジスタを使用すると、最初の命令は 5 バイト長または 6 バイト長になる可能性があります。
このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている LE モデルを実装します。
表 8–14 32 ビット x86: Local Executable スレッド固有変数のアクセスコード
コードシーケンス |
初期の再配置 |
シンボル |
0x00 movl %gs:0, %eax 0x06 leal x@ntpoff(%eax), %eax # %eax - contains address of TLS variable |
<none> R_386_TLS_LE |
x |
movl 命令は R_386_TLS_LE_32 再配置を生成します。リンカーは、実行可能ファイルで定義されたシンボルの静的な TLS オフセットに、この再配置を直接結合します。実行時には処理は不要です。
次の命令シーケンスを使用すると、同じ再配置により変数 x の (アドレスではなく) 内容にアクセスできます。
表 8–15 32 ビット x86: Local Executable スレッド固有変数のアクセスコード
コードシーケンス |
初期の再配置 |
シンボル |
0x00 movl %gs:0, %eax 0x06 movl x@ntpoff(%eax), %eax # %eax - contains address of TLS variable |
<none> R_386_TLS_LE |
x |
次のシーケンスを使用すると、変数のアドレスの計算ではなく、その変数からの読み込みやその変数への保存を実行できます。この例では、x@ntpoff による式を即値としてではなく絶対アドレスとして使用しています。
表 8–16 32 ビット x86: Local Executable スレッド固有変数のアクセスコード
コードシーケンス |
初期の再配置 |
シンボル |
0x00 movl %gs:x@ntpoff, %eax # %eax - contains address of TLS variable |
R_386_TLS_LE |
x |
次の表に、x86 用に定義された TLS 再配置を示します。表内の説明では、次の表記が使用されています。
TLS_index 構造体を保持するために、GOT 内に連続した 2 つのエントリを割り当てます。この構造体は、___tls_get_addr() に渡されます。このエントリを参照する命令は、2 つの GOT エントリのうちの最初のエントリに結合されます。
この再配置は、___tls_get_addr() 関数を参照する R_386_PLT32 再配置と同じように処理されます。
TLS_index 構造体を保持するために、GOT 内に連続した 2 つのエントリを割り当てます。この構造体は、___tls_get_addr() に渡されます。TLS_index の ti_tlsoffset フィールドは 0 に設定され、ti_moduleid は実行時に埋められます。___tls_get_addr() 呼び出しは、動的な TLS ブロックの開始オフセットを返します。
GOT に 1 つのエントリを割り当ててから、静的な TLS ブロックからの負の相対 tlsoffset を使用してこのエントリを初期化します。このシーケンスは、R_386_TLS_TPOFF 再配置を使用して実行時に行われます。
この式は @gotntpoff に似ていますが、位置に依存するコードで使用されます。@gotntpoff は、movl 命令内または addl 命令内の GOT の先頭からの相対 GOT スロットアドレスに解決されます。@indntpoff は、絶対 GOT スロットアドレスに解決されます。
静的な TLS ブロックからの負の相対 tlsoffset を計算します。
TLS ブロックからの相対 tlsoffset を計算します。この値は加数の即値として使用され、特定のレジスタには関連付けられません。
TLS シンボルを含むオブジェクトの識別子を計算します。
名前 |
値 |
フィールド |
計算 |
---|---|---|---|
R_386_TLS_GD_PLT |
12 |
Word32 |
@tlsgdplt |
R_386_TLS_LDM_PLT |
13 |
Word32 |
@tlsldmplt |
R_386_TLS_TPOFF |
14 |
Word32 |
@ntpoff(S) |
R_386_TLS_IE |
15 |
Word32 |
@indntpoff(S) |
R_386_TLS_GOTIE |
16 |
Word32 |
@gotntpoff(S) |
R_386_TLS_LE |
17 |
Word32 |
@ntpoff(S) |
R_386_TLS_GD |
18 |
Word32 |
@tlsgd(S) |
R_386_TLS_LDM |
19 |
Word32 |
@tlsldm(S) |
R_386_TLS_LDO_32 |
32 |
Word32 |
@dtpoff(S) |
R_386_TLS_DTPMOD32 |
35 |
Word32 |
@dtpmod(S) |
R_386_TLS_DTPOFF32 |
36 |
Word32 |
@dtpoff(S) |
x64 では、TLS へのアクセスに次のコードシーケンスモデルを使用できます。
このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている GD モデルを実装します。
表 8–18 x64: General Dynamic スレッド固有変数のアクセスコード
__tls_get_addr() 関数は、tls_index 構造体のアドレスという 1 つのパラメータを取ります。x@tlsgd(%rip) による式と関連付けられた R_AMD64_TLSGD 再配置は、tls_index 構造体を GOT 内で割り当てるように、リンカーに指示します。tls_index 構造体で必要な 2 つの要素は、連続する GOT エントリである GOT[n] および GOT[n+1] で保持されます。これらの GOT エントリは、R_AMD64_DTPMOD64 再配置と R_AMD64_DTPOFF64 再配置に関連付けられます。
アドレス 0x00 の命令は、最初の GOT エントリのアドレスを計算します。この計算により、リンク編集時に明らかになる GOT の最初の PC 相対アドレスが現在の命令のポインタに追加されます。結果は、%rdi レジスタを使用して __tls_get_addr() 関数に渡されます。
leaq 命令は、最初の GOT エントリのアドレスを計算します。この計算は、リンク編集時に決定された GOT の PC 相対アドレスを現在の命令のポインタに追加して行われます。.byte、.word、および .rex64 接頭辞によって、命令シーケンス全体で 16 バイトを占めることが確実になります。接頭辞が使用されるのは、コードに悪影響を及ぼすことがないからです。
このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている LD モデルを実装します。
表 8–19 x64: Local Dynamic スレッド固有変数のアクセスコード
最初の 2 つの命令は、パッドはありませんが、一般的な動的モデルに使用されるコードシーケンスと同じです。2 つの命令は必ず連続させてください。x1@tlsld(%rip) シーケンスは、シンボル x1 の tls_index エントリを生成します。このインデックスはオフセットがゼロ の x1 を含む現在のモジュールを参照します。リンカーは、オブジェクト R_AMD64_DTMOD64 の再配置を作成します。
オフセットは別々に読み込まれるので、R_AMD64_DTOFF32 再配置は不要です。x1@dtpoff による式は、シンボル x1 のオフセットにアクセスするために使用されます。この命令をアドレス 0x10 として使用して、完全なオフセットが読み込まれ %rax() 内の __tls_get_addr 呼び出しの結果に追加されて結果が %rcx に生成されます。x1@dtpoff による式は、R_AMD64_DTPOFF32 再配置を作成します。
次の命令を使用すると、変数のアドレスを計算するのではなく、変数の値を読み込むことができます。この命令は、元の leaq 命令と同じ再配置を作成します。
movq x1@dtpoff(%rax), %r11 |
TLS ブロックのベースアドレスがレジスタ内で保持されていると、保護されているスレッド固有変数のアドレスの読み込み、保存、または計算には 1 つの命令が必要となります。
固有の動的モデルを使用した場合には、一般的な動的モデルを使用した場合よりも利点があります。すべての追加スレッド固有変数アクセスに必要なのは、3 つの新しい命令だけです。さらに、GOT エントリの追加や実行時の再配置は必要ありません。
このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている IE モデルを実装します。
表 8–20 x64: Initial Executable スレッド固有変数のアクセスコード
コードシーケンス |
初期の再配置 |
シンボル |
0x00 movq %fs:0, %rax 0x09 addq x@gottpoff(%rip), %rax # %rax - contains address of TLS variable |
<none> R_AMD64_GOTTPOFF |
x |
未処理の再配置 |
シンボル |
|
GOT[n] |
R_AMD64_TPOFF64 |
x |
シンボル x の R_AMD64_GOTTPOFF 再配置は、リンカーに GOT エントリおよび関連する R_AMD64_TPOFF64 再配置の生成を要求します。その後、この命令は x@gottpoff(%rip) 命令の最後からの GOT エントリの相対オフセットを使用します。R_AMD64_TPOFF64 再配置は、現在読み込まれているモジュールから決定されるシンボル x の値を使用します。オフセットは GOT エントリに書き込まれたあとで、addq 命令によって読み込まれます。
x のアドレスではなく x の内容を読み込む場合は、次のシーケンスを使用できます。
表 8–21 x64: Initial Executable スレッド固有変数のアクセスコード II
コードシーケンス |
初期の再配置 |
シンボル |
0x00 movq x@gottpoff(%rip), %rax 0x06 movq %fs:(%rax), %rax # %rax - contains contents of TLS variable |
R_AMD64_GOTTPOFF <none> |
x |
未処理の再配置 |
シンボル |
|
GOT[n] |
R_AMD64_TPOFF64 |
x |
このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている LE モデルを実装します。
表 8–22 x64: Local Executable スレッド固有変数のアクセスコード
コードシーケンス |
初期の再配置 |
シンボル |
0x00 movq %fs:0, %rax 0x06 leaq x@tpoff(%rax), %rax # %rax - contains address of TLS variable |
<none> R_AMD64_TPOFF32 |
x |
TLS 変数のアドレスではなく TLS 変数の内容を読み込む場合は、次のシーケンスを使用できます。
表 8–23 x64: Local Executable スレッド固有変数のアクセスコード II
コードシーケンス |
初期の再配置 |
シンボル |
0x00 movq %fs:0, %rax 0x06 movq x@tpoff(%rax), %rax # %rax - contains contents of TLS variable |
<none> R_AMD64_TPOFF32 |
x |
次のシーケンスはより短いものです。
表 8–24 x64: Local Executable スレッド固有変数のアクセスコード III
コードシーケンス |
初期の再配置 |
シンボル |
0x00 movq %fs:x@tpoff, %rax # %rax - contains contents of TLS variable |
R_AMD64_TPOFF32 |
x |
次の表に、x64 用に定義された TLS 再配置を示します。表内の説明では、次の表記が使用されています。
TLS_index 構造体を保持するために、GOT 内に連続した 2 つのエントリを割り当てます。この構造体は、__tls_get_addr() に渡されます。この命令は、正確で一般的な動的コードシーケンス内でのみ使用できます。
TLS_index 構造体を保持するために、GOT 内に連続した 2 つのエントリを割り当てます。この構造体は、__tls_get_addr() に渡されます。実行時、オブジェクトの ti_offset オフセットフィールドは ゼロに設定され、ti_module オフセットは初期化されます。__tls_get_addr() 関数の呼び出しは、動的な TLS ブロックの開始オフセットを返します。この命令は、正確なコードシーケンス内で使用できます。
変数を含む TLS ブロックの最初からの変数の相対オフセットを計算します。計算値は加数の即値として使用され、特定のレジスタには関連付けられません。
TLS シンボルを含むオブジェクトの識別子を計算します。
最初の TLS ブロック内の変数のオフセットを保持する GOT 内のエントリを割り当てます。このオフセットは TLS ブロックの端、%fs:0 から計算されます。この演算子は、movq または addq 命令とだけ使用できます。
TLS ブロックの端、%fs:0 からの変数の相対オフセットを計算します。GOT エントリは作成されません。
名前 |
値 |
フィールド |
計算 |
---|---|---|---|
R_AMD64_DPTMOD64 |
16 |
Word64 |
@dtpmod(s) |
R_AMD64_DTPOFF64 |
17 |
Word64 |
@dtpoff(s) |
R_AMD64_TPOFF64 |
18 |
Word64 |
@tpoff(s) |
R_AMD64_TLSGD |
19 |
Word32 |
@tlsgd(s) |
R_AMD64_TLSLD |
20 |
Word32 |
@tlsld(s) |
R_AMD64_DTPOFF32 |
21 |
Word32 |
@dtpoff(s) |
R_AMD64_GOTTPOFF |
22 |
Word32 |
@gottpoff(s) |
R_AMD64_TPOFF32 |
23 |
Word32 |
@gottpoff(s) |