リンカーとライブラリ

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