在 x64 上,可使用以下代码序列模型来访问 TLS。
此代码序列实现线程局部存储的访问模型中介绍的 GD 模型。
表 8–18 x64: 常规动态线程局部变量的访问代码
__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_DTPMOD64 和 R_AMD64_DTPOFF64 重定位关联。
地址 0x00 处的指令计算第一个 GOT 项的地址。此计算将 GOT 起始位置的 PC 相对地址(在链接编辑时确定)与当前指令指针相加。使用 %rdi 寄存器将结果传递给 __tls_get_addr() 函数。
leaq 指令计算第一个 GOT 项的地址。将 GOT 的 PC 相对地址(在链接编辑时确定)与当前指令指针相加来执行此计算。.byte、.word 和 .rex64 前缀确保整个指令序列占用 16 字节。由于这些前缀不会对代码造成负面影响,因此可以使用这些前缀。
此代码序列实现线程局部存储的访问模型中介绍的 LD 模型。
表 8–19 x64: 局部动态线程局部变量的访问代码
前两个指令与用于常规动态模型的代码序列等效,虽然不进行任何填充。这两个指令必须是连续的。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 项或运行时重定位。
此代码序列实现线程局部存储的访问模型中介绍的 IE 模型。
表 8–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 |
符号 x 的 R_AMD64_GOTTPOFF 重定位请求链接编辑器生成 GOT 项和关联的 R_AMD64_TPOFF64 重定位。然后,x@gottpoff(%rip) 指令使用 GOT 项相对于此指令结束位置的偏移。R_AMD64_TPOFF64 重定位使用根据目前所装入模块确定的符号 x 的值。偏移将写入到 GOT 项中,然后由 addq 指令装入。
要装入 x 的内容(而不是 x 的地址),可使用以下序列。
表 8–21 x64: 初始可执行的线程局部变量的访问代码 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: 局部可执行的线程局部变量的访问代码
代码序列 |
初始重定位 |
符号 |
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: 局部可执行的线程局部变量的访问代码 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: 局部可执行的线程局部变量的访问代码 III
代码序列 |
初始重定位 |
符号 |
0x00 movq %fs:x@tpoff, %rax # %rax - contains contents of TLS variable |
R_AMD64_TPOFF32 |
x |