Oracle® Solaris 11.2 链接程序和库指南

退出打印视图

更新时间: 2014 年 7 月
 
 

x64: 线程局部变量访问

在 x64 上,可使用以下代码序列模型访问 TLS。

x64: 常规动态 (General Dynamic, GD)

此代码序列实现线程局部存储的访问模型中介绍的 GD 模型。

表 14-18  x64: 常规动态的线程局部变量访问代码
代码序列
初始重定位
符号
0x00 .byte 0x66
0x01 leaq  x@tlsgd(%rip), %rdi
0x08 .word 0x6666
0x0a rex64
0x0b call  __tls_get_addr@plt

# %rax - 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 结构的地址。与 x@tlsgd(%rip) 表达式关联的 R_AMD64_TLSGD 重定位指示链接编辑器在 GOT 中分配 tls_index 结构。tls_index 结构所需的两个元素将保留在连续的 GOT 项中(GOT[n]GOT[n+1])。这些 GOT 项与 R_AMD64_DTPMOD64R_AMD64_DTPOFF64 重定位相关联。

地址 0x00 处的指令计算第一个 GOT 项的地址。通过将 GOT 起始位置的 PC 相对地址(在链接编辑时得知)与当前指令指针相加进行此计算。结果将通过 %rdi 寄存器传递给 __tls_get_addr() 函数。


注 - leaq 指令计算第一个 GOT 项的地址。通过将 GOT 的 PC 相对地址(在链接编辑时得知)与当前指令指针相加进行此计算。.byte.word、和 .rex64 前缀可确保整个指令序列占用 16 个字节。由于前缀不会对代码造成负面影响,因此可以使用前缀。

x64: 局部动态 (Local Dynamic, LD)

此代码序列实现线程局部存储的访问模型中介绍的 LD 模型。

表 14-19  x64: 局部动态的线程局部变量访问代码
代码序列
初始重定位
符号
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

# %r9 - 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

前两个指令与用于常规动态模型的代码序列等效,虽然不进行任何填充。这两个指令必须是连续的。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 块的基本地址保存在一个寄存器中,则装入、存储或计算受保护的线程局部变量的地址将只需要一个指令。

使用局部动态模型比使用常规动态模型可获得更多好处。每进行一次额外的线程局部变量访问,只需执行三个新指令。此外,不需要生成其他 GOT 项或运行时重定位。

x64: 初始可执行 (Initial Executable, IE)

此代码序列实现线程局部存储的访问模型中介绍的 IE 模型。

表 14-20  x64: 初始可执行的线程局部变量访问代码
代码序列
初始重定位
符号
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 的地址),可使用以下序列。

表 14-21  x64: 初始可执行的线程局部变量访问代码 II
代码序列
初始重定位
符号
0x00 movq  x@gottpoff(%rip), %rax
0x07 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 模型。

表 14-22  x64: 局部可执行的线程局部变量访问代码
代码序列
初始重定位
符号
0x00 movq  %fs:0, %rax
0x09 leaq  x@tpoff(%rax), %rax

# %rax - contains address of TLS variable
<none>
R_AMD64_TPOFF32
x

要装入 TLS 变量的内容(而非 TLS 变量的地址),可使用以下序列。

表 14-23  x64: 局部可执行的线程局部变量的访问代码 II
代码序列
初始重定位
符号
0x00 movq  %fs:0, %rax
0x09 movq  x@tpoff(%rax), %rax

# %rax - contains contents of TLS variable
<none>
R_AMD64_TPOFF32
x

以下序列更为简短。

表 14-24  x64: 局部可执行的线程局部变量访问代码 III
代码序列
初始重定位
符号
0x00 movq  %fs:x@tpoff, %rax

# %rax - contains contents of TLS variable
R_AMD64_TPOFF32
x