リンカーとライブラリ

スレッド固有領域のアクセスモデル

各 TLS 参照は、 以下のアクセスモデルのどれかになります。ここでは、もっとも一般的なモデル (しかし最適化の程度はもっとも低い) から順に、もっとも高速なモデル (しかし制限度は高い) へと並んでいます。

General Dynamic (GD) - 動的な TLS

このモデルでは、共有オブジェクトまたは動的実行可能ファイルから、すべての TLS 変数を参照できます。このモデルでは、TLS ブロックが特定のスレッドから初めて参照される時まで、このブロックの割り当てを延期することもできます。

Local Dynamic (LD) - ローカルシンボルの動的な TLS

このモデルは、GD モデルを最適化したものです。構築される動的オブジェクト内で変数がローカルに結合されているか、あるいは保護されていることが確認された場合、コンパイラは、動的な tlsoffsetを静的に結合してこのモデルを使用するように、リンカーに指示します。これにより、GD モデルを上回る性能が得られます。dtv0,m のアドレスの確認は、関数ごとに tls_get_addr() を 1 度呼び出すだけです。リンク編集時に結合される動的な TLS オフセットは、参照ごとに dtv0,m アドレスに追加されます。

Initial Executable (IE) - オフセットが割り当てられた静的な TLS

このモデルは、初期の静的な TLS テンプレートの一部として利用できる TLS 変数だけを参照できます。このテンプレートは、プロセスの起動時に使用できるすべての TLS ブロックで構成されます。このモデルでは、変数 x の、スレッドポインタからの相対オフセットは、x の大域オフセットテーブルエントリ内に保存されます。このモデルは、当初のプロセス起動のあとで遅延読み込み、フィルタ、dlopen(3DL) などを介して読み込まれる共有ライブラリからは、TLS 変数を参照できません。このモデルは、遅延割り当てを使用する TLS ブロックにアクセスできません。

Local Executable (LE) - 静的な TLS

このモデルは、動的実行可能ファイル自体の TLS ブロックの一部である TLS 変数だけを参照できます。リンカーは、動的な再配置や大域オフセットテーブルの参照を別途行うことなく、スレッドポインタからの相対オフセットを静的に計算します。このモデルを使用して動的実行可能ファイルの外部に存在する変数を参照することはできません。

リンカーは、妥当と判断する場合は、比較的一般的なアクセスモデルから、より最適化されたモデルへとコードを移行できます。この移行は、独特な TLS 再配置を使用することで行えます。これらの再配置は、更新を要求するだけでなく、どの TLS アクセスモデルが使用されているかの特定もします。

TLS アクセスモデルと、作成されるオブジェクトのタイプを認識することで、リンカーは変換が可能となります。たとえば、GD アクセスモデルを使用している再配置可能オブジェクトが動的実行可能ファイルにリンクされている場合、リンカーは IE アクセスモデルまたは LE アクセスモデルを使用して参照を適宜移行できます。そのあとで、そのモデルに必要な再配置が行われます。

次の図は、それぞれのアクセスモデルと、どの時点でモデル間の移行が可能であるかを示しています。

図 8–2 スレッド固有領域のアクセスモデルと移行

スレッド固有領域のアクセスモデルと移行。

SPARC: スレッド固有変数へのアクセス

SPARC では、スレッド固有変数へのアクセスに以下のコードシーケンスモデルを使用できます。

SPARC: 32 ビットおよび 64 ビットの General Dynamic (GD)

このコードシーケンスはもっとも一般的であり、共有オブジェクトと動的実行可能ファイルのどちらでも使用できます。このコードシーケンスは、共有オブジェクト内および動的実行可能ファイル内の外部 TLS 変数も参照できます。

表 8–2 SPARC: 32 ビットおよび 64 ビットの General Dynamic スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

# %l7 - GOT ポインタに初期化されている

0x00 sethi %hi(@dtlndx(x)), %o0
0x04 add   %o0,%lo(@dtlndx(x)),%o0
0x08 add   %l7, %o0, %o0
0x0c call  x@TLSPLT
 
# %o0 - TLS 変数のアドレスが含まれる
 
 
R_SPARC_TLS_GD_HI22
R_SPARC_TLS_GD_LO22
R_SPARC_TLS_GD_ADD
R_SPARC_TLS_GD_CALL
 
 
x
x
x
x
 

未処理の再配置: 32 ビット 

シンボル 

GOT[n]
GOT[n + 1]
R_SPARC_TLS_DTPMOD32
R_SPARC_TLS_DTPOFF32
x
x
 

未処理の再配置: 64 ビット 

シンボル 

GOT[n]
GOT[n + 1]
R_SPARC_TLS_DTPMOD64
R_SPARC_TLS_DTPOFF64
x
x

sethi 命令は R_SPARC_TLS_GD_HI22 再配置を生成し、add 命令は R_SPARC_TLS_GD_LO10 再配置を生成します。これらの再配置は、変数 x への TLS_index 構造体を保持する領域を大域オフセットテーブル内に割り当てるように、リンカーに指示します。リンカーは、この新しい GOT エントリに GOT からの相対オフセットを代入することによって、この再配置を処理します。

読み込みオブジェクトインデックスと x の TLS ブロックインデックスは実行時まで不明なため、リンカーは、実行時リンカーによって処理されるように、GOT に対する R_SPARC_TLS_DTPMOD32 再配置と R_SPARC_TLS_DPTOFF32 再配置を設定します。

add 命令は、R_SPARC_TLS_GD_ADD 再配置を生成します。この再配置が使用されるのは、リンカーによって GD コードシーケンスがほかのシーケンスに変更される場合だけです。

call 命令は、R_SPARC_TLS_GD_CALL 再配置を生成します。この再配置は、__tls_get_addr () 関数の呼び出しを結合するようリンカーに指示し、call 命令を GD コードシーケンスに関連付けます。


注 –

add 命令は、call 命令の前に指定する必要があります。この命令を、呼び出しの遅延スロットに配置することはできません。これは、あとで発生するコード変換が既知の順序を必要とするためです。

R_SPARC_TLS_GD_ADD 再配置によってタグが付けられた add 命令の GOT ポインタとして使用されるレジスタは、add 命令内の最初のレジスタでなければなりません。このように指定することで、リンカーはコード変換時に GOT ポインタであるレジスタを識別できるようになります。


SPARC: 32 ビットおよび 64 ビットの Local Dynamic (LD)

このコードシーケンスは、共有オブジェクトまたは動的実行可能ファイルで使用できます。このシーケンスは、参照として同じオブジェクト内に結合された TLS 変数を参照する場合に使用します。動的な tlsoffset はリンク編集時に結合が可能なため、__tls_get_addr() の呼び出しは LD コードシーケンスを介して参照されるすべてのシンボルの関数呼び出しごとに 1 回だけですみます。

表 8–3 SPARC: 32 ビットおよび 64 ビットの Local Dynamic スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

# %l7 - GOT ポインタに初期化されている


0x00 sethi %hi(@tmndx(x1)), %o0
0x04 add   %o0,%lo(@tmndx(x1)),%o0
0x08 add   %l7, %o0, %o0
0x0c call  x@TLSPL
R_SPARC_TLS_LDM_HI22
R_SPARC_TLS_LDM_LO10
R_SPARC_TLS_LDM_ADD
R_SPARC_TLS_LDM_CALL
x1
x1
x1
x1
# %o0 - 現在のオブジェクトの TLS ブロックのアドレスが含まれる
 
0x10 sethi %hi(@dtpoff(x1)), %l1
0x14 xor   %l1, %lo(@dtpoff(x1)), %l1
0x18 add   %o0, %l1, %l1
R_SPARC_TLS_LDO_HIX22
R_SPARC_TLS_LDO_LOX10
R_SPARC_TLS_LDO_ADD
x1
x1
x1
# %l1 - ローカル TLS 変数 x1 のアドレスが含まれる
 
0x20 sethi %hi(@dtpoff(x2)), %l2
0x24 xor   %l2, %lo(@dtpoff(x2)), %l2
0x28 add   %o0, %l2, %l2
R_SPARC_TLS_LDO_HIX22
R_SPARC_TLS_LDO_LOX10
R_SPARC_TLS_LDO_ADD
x2
x2
x2
# %l2 - ローカル TLS 変数 x2 のアドレスが含まれる
 
 

未処理の再配置: 32 ビット 

シンボル 

GOT[n]
GOT[n + 1]
R_SPARC_TLS_DTPMOD32
<なし>
x1
 

未処理の再配置: 64 ビット 

シンボル 

GOT[n]
GOT[n + 1]
R_SPARC_TLS_DTPMOD64
<なし>
x1

最初の sethi 命令は R_SPARC_TLS_LDM_HI22 再配置を生成し、add 命令は R_SPARC_TLS_LDM_LO10 再配置を生成します。これらの再配置は、現在のオブジェクトのTLS_index 構造体を保持する領域を大域オフセットテーブル内に割り当てるように、リンカーに指示します。リンカーは、この新しい 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 再配置を生成し、or 命令は R_SPARC_TLS_LDO_LOX10 再配置を生成します。各ローカルシンボルの TLS オフセットはリンク編集時に認識されるため、これらの値は直接埋め込まれます。add 命令には、R_SPARC_TLS_LDO_ADD 再配置によってタグが付けられます。

手続きが複数のローカルシンボルを参照する場合には、コンパイラは TLS ブロックの基底アドレスを 1 度だけ取得するコードを生成します。以後、各シンボルのアドレスの計算にはこの基底アドレスが使用され、個別にライブラリを呼び出すことはありません。


注 –

R_SPARC_TLS_LDO_ADD によってタグが付けられた add 命令内の TLS オブジェクトアドレスが入ったレジスタは、命令シーケンス内の最初のレジスタでなければなりません。このように指定することで、リンカーはコード変換時にレジスタを識別できるようになります。


SPARC: 32 ビットの Initial Executable (IE)

このコードシーケンスは、動的実行可能ファイルでのみ使用できます。このモデルは、その実行可能ファイル内、またはプロセスの起動時に読み込まれる任意の共有ライブラリ内で定義された TLS 変数を参照できます。このモデルでは、プロセスの起動後に読み込まれる共有ライブラリ内の TLS 変数を参照することはできません。

表 8–4 SPARC: 32 ビットの Initial Executable スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

# %l7 - GOT ポインタに初期化されている

0x00 sethi %hi(@tpoff(x)), %o0
0x04 or    %o0,%lo(@tpoff(x)),%o0
0x08 ld    [%l7 + %o0], %o0
0x0c add   %g7, %o0, %o0
 
# %o0 - TLS 変数のアドレスが含まれる
 
 
R_SPARC_TLS_IE_HI22
R_SPARC_TLS_IE_LO10
R_SPARC_TLS_IE_LD
R_SPARC_TLS_IE_ADD
 
 
x
x
x
x
 

未処理の再配置 

シンボル 

GOT[n]
R_SPARC_TLS_TPOFF32
x

sethi 命令は R_SPARC_TLS_IE_HI22 再配置を生成し、or 命令は R_SPARC_TLS_IE_LO10 再配置を生成します。これらの再配置は、シンボル x の静的な TLS オフセットを保存する領域を大域オフセットテーブル内に作成するように、リンカーに指示します。このとき、GOT に対する R_SPARC_TLS_TPOFF32 の再配置は、未処理の状態に置かれます。あとで、実行時リンカーがシンボル x の負の静的 TLS オフセットを埋め込みます。ld 命令には R_SPARC_TLS_IE_LD 再配置によってタグが付けられ、add 命令には R_SPARC_TLS_IE_ADD 再配置によってタグが付けられます。


注 –

R_SPARC_TLS_IE_ADD 再配置によってタグが付けられた add 命令の GOT ポインタとして使用されるレジスタは、この命令内の最初のレジスタでなければなりません。このように指定することで、リンカーはコード変換時に GOT ポインタであるレジスタを識別できるようになります。


SPARC: 64 ビットの Initial Executable (IE)

このシーケンスは、ld 命令ではなく ldx 命令を使用して 64 ビットアドレスを読み込んでいる点を除き、SPARC 32 ビットシーケンスと同じです。

表 8–5 SPARC: 64 ビットの Initial Executable スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

# %l7 - GOT ポインタに初期化されている

0x00 sethi %hi(@tpoff(x)), %o0
0x04 or    %o0,%lo(@tpoff(x)),%o0
0x08 ldx   [%l7 + %o0], %o0
0x0c add   %g7, %o0, %o0
 
# %o0 - TLS 変数のアドレスが含まれる
 
 
R_SPARC_TLS_IE_HI22
R_SPARC_TLS_IE_LO10
R_SPARC_TLS_IE_LD
R_SPARC_TLS_IE_ADD
 
 
x
x
x
x
 

未処理の再配置 

シンボル 

GOT[n]
R_SPARC_TLS_TPOFF64
x

SPARC: 32 ビットおよび 64 ビットの Local Executable (LE)

このコードシーケンスは、動的実行可能ファイル内から、同じファイル内で定義された TLS 変数を参照するためにのみ使用できます。この場合、静的な tlsoffset はリンク編集時に認識され、実行時の再配置は不要です。

表 8–6 SPARC: 32 ビットおよび 64 ビットの Local Executable スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

0x00 sethi %hix(@tpoff(x)), %o0
0x04 xor   %o0,%lo(@tpoff(x)),%o0
0x08 add   %g7, %o0, %o0
 
# %o0 - TLS 変数のアドレスが含まれる
R_SPARC_TLS_LE_HIX22
R_SPARC_TLS_LE_LOX10
<なし>
x
x

sethi 命令は R_SPARC_TLS_LE_HIX22 再配置を生成し、or 命令は R_SPARC_TLS_LE_LOX10 再配置を生成します。リンカーは、実行可能ファイルで定義されたシンボルの静的な TLS オフセットに、これらの再配置を直接結合します。実行時には、再配置処理は不要です。

SPARC: スレッド固有領域の再配置のタイプ

次の表に、SPARC 用に定義された TLS 再配置を示します。この表の説明は、次の表記規則を使用しています。

@dtlndx(x)

TLS_index 構造体を保持するために、大域オフセットテーブル内に連続した 2 つのエントリを割り当てます。この情報は、__tls_get_addr() に渡されます。このエントリを参照する命令は、2 つの GOT エントリのうちの最初のエントリのアドレスに結合されます。

@tmndx(x)

TLS_index 構造体を保持するために、大域オフセットテーブル内に連続した 2 つのエントリを割り当てます。この情報は、__tls_get_addr() に渡されます。この構造体の ti_tlsoffset フィールドは 0 に設定され、ti_moduleid は実行時に埋め込まれます。__tls_get_addr() 呼び出しは、動的な TLS ブロックの開始オフセットを返します。

@dtpoff(x)

TLS ブロックからの相対 tlsoffset を計算します。

@tpoff(x)

静的な TLS ブロックからの負の相対 tlsoffset を計算します。この値は、TLS アドレスを計算するためにスレッドポインタに追加されます。

@dtpmod(x)

シンボル S を含むオブジェクトの識別子を計算します。

表 8–7 SPARC: スレッド固有領域の再配置のタイプ

名前 

値 

フィールド 

計算 

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_ADD を参照

R_SPARC_TLS_GD_CALL

59

V-disp30

R_SPARC_TLS_GD_CALL を参照

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_ADD を参照

R_SPARC_TLS_LDM_CALL

63

V-disp30

R_SPARC_TLS_LDM_CALL を参照

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_LDO_ADD を参照

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_LD を参照

R_SPARC_TLS_IE_LDX

70

なし 

R_SPARC_TLS_IE_LDX を参照

R_SPARC_TLS_IE_ADD

71

なし 

R_SPARC_TLS_IE_ADD を参照

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)

いくつかの再配置型には、単純な計算を超えた意味が存在します。

R_SPARC_TLS_GD_ADD

この再配置は、GD コードシーケンスの add 命令にタグを付けます。GOT ポインタに使用されるレジスタは、シーケンス内の最初のレジスタです。この再配置によってタグが付けられる命令は、R_SPARC_TLS_GD_CALL 再配置によってタグが付けられる call 命令の前に置かれます。これは、リンク編集時に TLS モデルを移行するために使用されます。

R_SPARC_TLS_GD_CALL

この再配置は、__tls_get_addr() 関数を参照する R_SPARC_WPLT30 再配置と同じように処理されます。この再配置は、GD コードシーケンスの一部です。

R_SPARC_LDM_ADD

この再配置は、LD コードシーケンスの最初の add 命令にタグを付けます。GOT ポインタに使用されるレジスタは、シーケンス内の最初のレジスタです。この再配置によってタグが付けられる命令は、R_SPARC_TLS_GD_CALL 再配置によってタグが付けられる call 命令の前に置かれます。これは、リンク編集時に TLS モデルを移行するために使用されます。

R_SPARC_LDM_CALL

この再配置は、__tls_get_addr() 関数を参照する R_SPARC_WPLT30 再配置と同じように処理されます。この再配置は、LD コードシーケンスの一部です。

R_SPARC_LDO_ADD

この再配置は、LD コードシーケンス内の最後の add 命令にタグを付けます。コードシーケンスの先頭で計算されるオブジェクトアドレスを含むレジスタは、この命令における最初のレジスタです。これにより、リンカーはコード変換でこのレジスタを識別できるようになります。

R_SPARC_TLS_IE_LD

この再配置は、32 ビットの IE コードシーケンス内の ld 命令にタグを付けます。これは、リンク編集時に TLS モデルを移行するために使用されます。

R_SPARC_TLS_IE_LDX

この再配置は、64 ビットの IE コードシーケンス内の ldx 命令にタグを付けます。これは、リンク編集時に TLS モデルを移行するために使用されます。

R_SPARC_TLS_IE_ADD

この再配置は、IE コードシーケンス内の add 命令にタグを付けます。GOT ポインタに使用されるレジスタは、シーケンス内の最初のレジスタです。

x86: スレッド固有変数へのアクセス

x86 では、TLS へのアクセスに以下のコードシーケンスモデルを使用できます。

x86: General Dynamic (GD)

このコードシーケンスはもっとも一般的であり、共有オブジェクトと動的実行可能ファイルのどちらでも使用できます。このコードシーケンスは、共有オブジェクト内および動的実行可能ファイル内の外部 TLS 変数を参照できます。

表 8–8 x86: General Dynamic スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

0x00 leal  x@tlsgd(,%ebx,1),%eax
0x07 call  x@tlsgdplt  

# %eax - TLS 変数のアドレスが含まれる
R_386_TLS_GD
R_386_TLS_GD_PLT
x
x
 

未処理の再配置 

シンボル 

GOT[n]
GOT[n + 1]
R_386_TLS_DTPMOD32
R_386_TLS_DTPOFF32
x

leal 命令は R_386_TLS_GD 再配置を生成します。この再配置は、変数 xTLS_index 構造体を保持する領域を大域オフセットテーブル内に割り当てるよう、リンカーに指示します。リンカーは、この新しい 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 命令の直後に配置する必要があります。これは、コード変換を可能にするために必要です。

x86: Local Dynamic (LD)

このコードシーケンスは、共有オブジェクトまたは動的実行可能ファイルで使用できます。このシーケンスは、参照として同じオブジェクト内に結合された TLS 変数を参照する場合に使用します。動的な tlsoffset はリンク編集時に結合が可能なため、___tls_get_addr() の呼び出しは、LD コードシーケンスを介して参照されるすべてのシンボルの関数呼び出しごとに 1 回だけですみます。

表 8–9 x86: Local Dynamic スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

0x00 leal  x1@tlsldm(%ebx), %eax
0x06 call  x@tlsldmplt
R_386_TLS_LDM
R_386_TLS_LDM_PLT
x1
x1
# %eax - 現在のオブジェクトの TLS ブロックのアドレスが含まれる
 
0x10 leal x1@dtpoff(%eax), %edx
R_386_TLS_LDO_32
x1
# %edx - ローカル TLS 変数 x1 のアドレスが含まれる
 
0x20 leal x2@dtpoff(%eax), %edx
R_386_TLS_LDO_32
x2
# %edx - ローカル TLS 変数 x2 のアドレスが含まれる
 
 

未処理の再配置 

シンボル 

GOT[n]
GOT[n + 1]
R_386_TLS_DTPMOD32
<なし>
x

最初の leal 命令は R_386_TLS_LDM 再配置を生成します。この再配置は、現在のオブジェクトの TLS_index 構造体を保持する領域を大域オフセットテーブル内に割り当てるよう、リンカーに指示します。リンカーは、この新しいリンクテーブルエントリに GOT からの相対オフセットを代入することによって、この再配置を処理します。

読み込みオブジェクトインデックスは実行時まで認識されないため、R_386_TLS_DTPMOD32 再配置が作成され、構造体の ti_tlsoffset フィールドにゼロが埋め込まれます。call 命令には、R_386_TLS_LDM_PLT 再配置によってタグが付けられます。

各ローカルシンボルの TLS オフセットはリンク編集時に認識されるため、リンカーはこれらの値を直接埋め込みます。

手続きが複数のローカルシンボルを参照する場合には、コンパイラは TLS ブロックの基底アドレスを取得するコードを 1 度だけ生成します。以後、各シンボルのアドレスの計算にはこの基底アドレスが使用され、個別にライブラリを呼び出すことはありません。

x86: Initial Executable (IE)

IE モデルには 2 つのコードシーケンスが存在します。その 1 つは、GOT ポインタを使用する、位置に依存しないコード用です。もう 1 つのシーケンスは、GOT ポインタを使用しない、位置に依存するコード用です。どちらのコードシーケンスも、動的実行可能ファイルでのみ使用できます。これらのコードシーケンスは、その実行可能ファイル内、またはプロセスの起動時に読み込まれる任意の共有ライブラリ内で定義された TLS 変数を参照できます。このモデルは、プロセスの起動後に読み込まれる共有ライブラリ内の TLS 変数を参照することはできません。

表 8–10 x86: 位置に依存しない Initial Executable スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

0x00 movl  %gs:0, %eax
0x06 addl  x@gotntpoff(%ebx), %eax

# %eax - TLS 変数のアドレスが含まれる
<なし>
R_386_TLS_GOTIE
 
x
 

未処理の再配置 

シンボル 

GOT[n]
R_386_TLS_TPOFF
x

addl 命令は R_386_TLS_GOTIE 再配置を生成します。この再配置は、シンボル x の静的な TLS オフセットを保存する領域を大域オフセットテーブル内に作成するよう、リンカーに指示します。このとき、GOT テーブルに対する R_386_TLS_TPOFF 再配置は、未処理の状態に置かれます。あとで、実行時リンカーがシンボル x の静的な TLS オフセットを埋め込みます。

表 8–11 x86: 位置に依存する Initial Executable スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

0x00 movl  %gs:0, %eax
0x06 addl  x@indntpoff, %eax

# %eax - TLS 変数のアドレスが含まれる
<なし>
R_386_TLS_IE
 
x
 

未処理の再配置 

シンボル 

GOT[n]
R_386_TLS_TPOFF
x

addl 命令は R_386_TLS_IE 再配置を生成します。この再配置は、シンボル x の静的な TLS オフセットを保存する領域を大域オフセットテーブル内に作成するよう、リンカーに指示します。このシーケンスと位置に依存しない形式との主な違いは、GOT ポインタレジスタのオフセットを介してではなく、作成される GOT エントリに直接、命令が結合されることです。このとき、GOT に対する R_386_TLS_TPOFF 再配置は、未処理の状態に置かれます。あとで、実行時リンカーがシンボル x の静的な TLS オフセットを埋め込みます。

次の 2 つのシーケンスに示すように、メモリー参照にオフセットを直接埋め込むことによって、変数 x の (アドレスではなく) 内容を読み込むことができます。

表 8–12 x86: 位置に依存しない Initial Executable 動的スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

0x00 movl  x@gotntpoff(%ebx), %eax
0x06 movl  %gs:(%eax), %eax

# %eax - TLS 変数のアドレスが含まれる
R_386_TLS_GOTIE
<なし>
x
 

未処理の再配置 

シンボル 

GOT[n]
R_386_TLS_TPOFF
x

表 8–13 x86: 位置に依存しない Initial Executable スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

0x00 movl  x@indntpoff, %ecx
0x06 movl  %gs:(%ecx), %eax

# %eax - TLS 変数のアドレスが含まれる
R_386_TLS_IE
<なし>
x
 

未処理の再配置 

シンボル 

GOT[n]
R_386_TLS_TPOFF
x

最後のシーケンスで、上記の %ecx ではなく %eax レジスタを使用すると、最初の命令は 5 バイト長または 6 バイト長になる可能性があります。

x86: Local Executable (LE)

このコードシーケンスは、動的実行可能ファイル内から、同じファイル内で定義された TLS 変数を参照するためにのみ使用できます。この場合、静的な tlsoffset はリンク編集時に認識され、実行時の再配置は不要です。

表 8–14 x86: Local Executable スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

0x00 movl %gs:0, %eax
0x06 leal x@ntpoff(%eax), %eax

# %eax - TLS 変数のアドレスが含まれる
<なし>
R_386_TLS_LE
 
x

movl 命令は、R_386_TLS_LE_32 再配置を生成します。リンカーは、実行可能ファイルで定義されたシンボルの静的な TLS オフセットに、この再配置を直接結合します。実行時には処理は不要です。

次の命令シーケンスを使用すると、同じ再配置により変数 x の (アドレスではなく) 内容にアクセスできます。

表 8–15 x86: Local Executable スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

0x00 movl %gs:0, %eax
0x06 movl x@ntpoff(%eax), %eax

# %eax - TLS 変数のアドレスが含まれる
<なし>
R_386_TLS_LE
 
x

変数のアドレスを計算するのではなく、その変数から読み込むかあるいはその変数に保存したいという場合は、次のシーケンスを使用できます。この例では、x@ntpoff による式を即値としてではなく絶対アドレスとして使用しています。

表 8–16 x86: Local Executable スレッド固有変数のアクセスコード

コードシーケンス 

初期の再配置 

シンボル 

0x00 movl %gs:x@ntpoff, %eax

# %eax - TLS 変数のアドレスが含まれる
R_386_TLS_LE
x

x86: スレッド固有領域の再配置のタイプ

次の表に、x86 用に定義された TLS 再配置を示します。この表の説明は、次の表記規則を使用しています。

@tlsgd(x)

TLS_index 構造体を保持するために、GOT 内に連続した 2 つのエントリを割り当てます。この構造体は、___tls_get_addr() に渡されます。このエントリを参照する命令は、2 つの GOT エントリのうちの最初のエントリに結合されます。

@tlsgdplt(x)

この再配置は、___tls_get_addr() 関数を参照する R_386_PLT32 再配置と同じように処理されます。

@tlsldm(x)

TLS_index 構造体を保持するために、GOT 内に連続した 2 つのエントリを割り当てます。この構造体は、___tls_get_addr() に渡されます。TLS_indexti_tlsoffset フィールドは 0 に設定され、ti_moduleid は実行時に埋められます。___tls_get_addr() 呼び出しは、動的な TLS ブロックの開始オフセットを返します。

@gotntpoff(x)

GOT 内に 1 つのエントリを割り当ててから、静的な TLS ブロックからの負の相対 tlsoffset を使用してこれを初期化します。これは、R_386_TLS_TPOFF 再配置を介して実行時に行われます。

@indntpoff(x)

この式は @gotntpoff に似ていますが、位置に依存するコードで使用されます。@gotntpoff は、movl 命令内または addl 命令内の GOT の先頭からの相対 GOT スロットアドレスに解決されます。@indntpoff は、絶対 GOT スロットアドレスに解決されます。

@ntpoff(x)

静的な TLS ブロックからの負の相対 tlsoffset を計算します。

@dtpoff(x)

TLS ブロックからの相対 tlsoffset を計算します。この値は加数の即値として使用され、特定のレジスタには関連付けられません。

@dtpmod(x)

シンボル S が含まれるオブジェクトの識別子を計算します。

表 8–17 x86: スレッド固有領域の再配置のタイプ

名前 

値 

フィールド 

計算 

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)