ZHCADC4A September 2011 – March 2014
该访问模型是通用动态模型的优化,用于访问模块自带数据。如果编译器知道自己正在访问模块自带的线程局部存储,则可使用该访问模型。如果线程局部变量是在访问自己的同一模块中定义的,则 TLS 偏移量在静态链接时是已知的。但是,模块 id 在静态链接时是未知的。
使用为零的偏移量实参来调用 __tls_get_addr(),将返回该模块 TLS 块的基址。该基址可用于访问属于该模块的所有线程局部数据。
在编译时,使用符号绑定和可见性来标识线程自带数据。具有静态范围或隐藏/受保护可见性的符号是自带数据。在该模型中,可按如下方式访问线程局部 x:
LDW *+DP($GOT_TLSMOD(x)), A4 ; reloc R_C6000_SBR_GOT_U15_W_TLSMOD
MVK $TBR_word(x), A5 ; reloc R_C6000_TBR_U15_W
|| CALLP __tls_get_addr,B3 ; A4 has the address of x at return
如前文所述,可以一次性获取自带 TLS 基址,然后重复用于访问其他自带线程局部变量,如下所示:
LDW *+DP($GOT_TLSMOD()), A4 ; reloc R_C6000_SBR_GOT_U15_W_TLSMOD w/ Symbol=0
MVK 0x0, A5 ;
|| CALLP __tls_get_addr,B3 ; A4 has the module’s own TLS base
MVK $TBR_byte(x), A5 ; reloc R_C6000_TBR_U15_B; Get x’s scaled TLS offset
LDB *A4[A5], A6 ; A6 has the value of thread-local char x
MVK $TBR_hword(y), A5 ; reloc R_C6000_TBR_U15_H; Get y’s scaled TLS offset
LDH *A4[A5], A6 ; A6 has the value of thread-local short y
MVK $TBR_word(z), A5 ; reloc R_C6000_TBR_U15_W; Get z’s scaled TLS offset
LDW *A4[A5], A6 ; A6 has the value of thread-local int z
MVK $TBR_dword(l), A5 ; reloc R_C6000_TBR_U15_D; Get l’s scaled TLS offset
LDDW *A4[A5], A7:A6 ; A7:A6 has the value of thread-local long long l
符号为零时,重定位 R_C6000_SBR_GOT_U15_W_TLSMOD 解析为模块的自带模块 id。TBR_U15 重定位对来自模块 TLS 基址的 15 位无符号偏移量进行编码,用于 near TB(TLS 块基址)寻址。重定位根据访问宽度进行缩放。前面的寻址可访问大小为 32KB 的 TLS 块。该规范将每个模块 TLS 块的大小限制为 32KB,预计这一限制足以满足大多数用例。因此,未定义 far TB 相对地址。可定义 far TBR 寻址,但这最多会使用 8 个新的重定位,最好是保留 ELF 允许的有限重定位数量 (256)。
静态链接器使用纯静态重定位来解析所有 TBR 重定位。也就是说,这些重定位不能位于动态重定位表中。