リンカーとライブラリ

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

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

x64: General Dynamic (GD)

このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている GD モデルを実装します。

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

コードシーケンス

初期の再配置

シンボル

0x00 .byte 0x66
0x01 leaq  x@tlsgd(%rip), %rdi
0x08 .word 0x666
0x0a rex64
0x0b call  __tls_get_addr@plt

# %iax - contains address of TLS variable
<none>
R_AMD64_TLSGD
<none>
<none>
R_AMD64_PLT32
 
x


__tls_get_addr
 

未処理の再配置

シンボル

GOT[n]
GOT[n + 1]
R_AMD64_DTPMOD64
R_AMD64_DTPOFF64
x
x

__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 バイトを占めることが確実になります。接頭辞が使用されるのは、コードに悪影響を及ぼすことがないからです。


x64: Local Dynamic (LD)

このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている LD モデルを実装します。

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

コードシーケンス

初期の再配置

シンボル

0x00 leaq  x1@tlsld(%rip), %rdi
0x07 call  __tls_get_addr@plt

# %rax - contains address of TLS block

0x10 leaq  x1@dtpoff(%rax), %rcx

# %rcx - contains address of TLS variable x1

0x20 leaq  x2@dtpoff(%rax), %r9

# %rcx - contains address of TLS variable x2
R_AMD64_TLSLD
R_AMD64_PLT32
 


R_AMD64_DTOFF32
 


R_AMD64_DTOFF32
x1
__tls_get_addr



 
x1


 
x2
 

未処理の再配置

シンボル

GOT[n]
R_AMD64_DTMOD64
x1

最初の 2 つの命令は、パッドはありませんが、一般的な動的モデルに使用されるコードシーケンスと同じです。2 つの命令は必ず連続させてください。x1@tlsld(%rip) シーケンスは、シンボル x1tls_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 エントリの追加や実行時の再配置は必要ありません。

x64: Initial Executable (IE)

このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている 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

シンボル xR_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

x64: Local Executable (LE)

このコードシーケンスは、「スレッド固有領域のアクセスモデル」で説明されている 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