Alpha 的链接器优化要比 IA-32 或 SPARC 的干净,因为对指令的次序没有限制。
重定位 TLSGD/TLSLDM , LTERAL 及 LITUSE ,在汇编文件中,由序号( sequence number )关联。这使得它们在目标文件中被连续发布( emit )。
不会发生 __tls_get_addr 模式的放宽,除非重定位以 TLSGD , LITERAL 及 LITUSE_TLSGD ,这个次序出现(对于 TLSLDM ,类似)。这用于区别 TLSGD 重定位不与任何调用序列关联的情形。汇编器将强制一个限制:如果存在 LITUSE_TLSGD ,将出现重定位 TLSGD 及 LITERAL ,而没有其它 LITUSE 重定位与 LITERAL 关联。
__tls_get_addr 模式的放宽要求,在紧跟 jsr 的偏移上,有一个 GPDISP 重定位。
常规动态到初始可执行
GD à IE 代码转换 |
初始重定位 符号 |
0x00 lda $16, x($gp) !tlsgd!1 0x04 ldq $27, __tls_get_addr ($gp) !literal!1 0x08 jsr $26, ($27), 0 !lituse_tlsgd!1 0x0c ldah $29, 0 ($26) !gpdisp!2 0x10 lda $29, 0 ($29) !gpdisp!2 ⇓ 0x00 ldq $16, x($gp) 0x04 nop 0x08 call_pal PAL_rduniq 0x0c addq $16, $0, $0 0x10 unop |
R_ALPHA_TLSGD x R_ALPHA_LITERAL __tls_get_addr R_ALPHA_LITUSE 4 R_ALPHA_GPDISP 4
⇓ ⇓ R_ALPHA_GOTTPREL x |
GOT [n] |
未解决的重定位 R_ALPHA_TPOFF64 x |
常规动态到局部可执行
GD à LE 代码转换 |
初始重定位 符号 |
0x00 lda $16, x($gp) !tlsgd!1 0x04 ldq $27, __tls_get_addr ($gp) !literal!1 0x08 jsr $26, ($27), 0 !lituse_tlsgd!1 0x0c ldah $29, 0 ($26) !gpdisp!2 0x10 lda $29, 0 ($29) !gpdisp!2 ⇓ 0x00 ldah $16, x($31) 0x04 lda $16, x($16) 0x08 call_pal PAL_rduniq 0x0c addq $16, $0, $0 0x10 unop |
R_ALPHA_TLSGD x R_ALPHA_LITERAL __tls_get_addr R_ALPHA_LITUSE 4 R_ALPHA_GPDISP 4
⇓ ⇓ R_ALPHA_TPRELHI x R_ALPHA_TPRELLO x |
|
未解决的重定位 |
如果 x 在 TLS 块中的偏移在 2G 以内,使用这个转换。如果偏移更大些,那么使用 GD à IE 的转换,除非没有动态重定位。
如果 x 在 TLS 块中的偏移在 32K 以内,那么第一条指令是一个 lda ,而第二条指令是一个 unop 。
局部动态到局部可执行
LD à LE 转换与 GD à LE 转换相同,除了我们引用模块 TLS 段的基址,而不是一个特定的变量。
初始可执行到局部可执行
IE à LE 代码转换 |
初始重定位 符号 |
0x00 ldq $1, x($gp) 0x04 addq $tp, $1, $0 ⇓ 0x00 lda $16, x($31) 0x04 addq $tp, $0, $0 |
R_ALPHA_GOTTPREL x
⇓ ⇓ R_ALPHA_TPREL x |
|
未解决的重定位 |
如果在 TLS 块中的偏移在 32K 以内,使用这个转换。如果偏移更大些,那么代码序列不变,但是在 GOT 的动态重定位被移除。
x86-64 链接器优化与 GNU 版本的 IA-32 优化非常接近。
常规动态到初始可执行
这个代码转换可以解释,在 x86-64 常规动态模式代码序列中的, 4 个填充字节。 IE 序列要多 4 个字节:
GD à IE 代码转换 |
初始重定位 符号 |
0x00 .byte 0x66 0x01 leaq x@tlsgd (%rip), %rdi 0x08 .word 0x6666 0x0a rex64 0x0b call __tls_get_add@plt ⇓ 0x00 movq %fs:0, %rax 0x09 addq x@gottpoff (%rip), %rax |
R_X86_64_TLSGD x
R_X86_64_PLT32 __tls_get_addr ⇓ ⇓
R_X86_64_GOTTPOFF x |
GOT [n] |
未解决的重定位 R_X86_64_TPOFF64 x |
常规动态到局部可执行
这个转换与前面的类似,只是偏移可以被直接存入指令:
GD à IE 代码转换 |
初始重定位 符号 |
0x00 .byte 0x66 0x01 leaq x@tlsgd (%rip), %rdi 0x08 .word 0x6666 0x0a rex64 0x0b call __tls_get_add@plt ⇓ 0x00 movq %fs:0, %rax 0x09 leaq x@tpoff (%rax), %rax |
R_X86_64_TLSGD x
R_X86_64_PLT32 __tls_get_addr ⇓ ⇓
R_X86_64_TPOFF32 x |
|
未解决的重定位 |
局部动态到局部可执行
下面的代码转换要求填充结果指令:
LD à LE 代码转换 |
初始重定位 符号 |
0x00 leaq x1@tlsld (%rip), %rdi 0x07 call __tls_get_add@plt … 0x10 leaq x1@dtpoff (%rax), %rcx ⇓ 0x00 .word 0x6666 0x02 .byte 0x66 0x03 movq %fs:0, %rax … 0x10 leaq x1@tpoff (%rax), %rdx |
R_X86_64_TLSGD x1 R_X86_64_PLT32 __tls_get_addr
R_X86_64_DTPOFF32 x1 ⇓ ⇓
R_X86_64_TPOFF32 x1 |
|
未解决的重定位 |
初始可执行到局部可执行
最后一个 x86-64 代码转换:
IE à LE 代码转换 |
初始重定位 符号 |
0x00 movq %fs:0, %rax 0x09 addq x@gottpoff (%rip), %rax ⇓ 0x00 movq %fs:0, %rax 0x09 leaq x@tpoff (%rip), %rax |
R_X86_64_GOTTPOFF x ⇓ ⇓
R_X86_64_TPOFF32 x |
|
未解决的重定位 |
s390 的 ABI 像 IA-32 那样,定义了四个链接器优化。这些优化解释函数 __tls_get_offset 。对于 s390 ,所有的代码序列基本上包括 3 样:
1. 提取线程指针
2. 获得变量到线程指针的偏移
3. 使用一个索引 / 基址操作数,在变量上,合并线程指针及偏移的操作(即, la %rx, 0 (%ry, %rz) )。
所有的优化所要做的,是改变获取偏移的方法。
常规动态到初始可执行
常规动态访问模式是代价最高的,这使得这个转换成为最重要的一个。对于常规动态访问模式,代码必须从字常数库载入一个 GOT 偏移,然后调用 __tls_get_offset ,从线程指针获得变量的偏移。对于初始可执行访问模式,代码需要载入一个包含变量到线程指针的偏移的 GOT 项。初始可执行代码的一个变种,把一个字常数库项用于 GOT 偏移。这使得转换变得简单,函数调用指令被一个载入指令替代,而字常数库常量 x@tlsgd 替代了 x@gotntpoff :
GD à IE 代码转换 |
初始重定位 符号 |
l %r6, .L1-.L0 (%r13) ear %r7, %a |
R_390_TLS_GDCALL x
R_390_TLS_GD32 x ⇓ ⇓
R_390_TLS_LOAD x
R_390_TLS_GOTIE32 x |
l %r2, .L2-.L0 (%r13) bas %r14, 0 (%r6, %r13) |
|
la %r8, 0 (%r2, %r7) # %r8 = &x ... .L0 : # literal pool, address in %r13 .L1: .long [email protected] .L2: .long x@ltsgd ⇓ l %r6, .L1-.L0 (%r13) ear %r7, %a |
|
l %r2, .L2-.L0 (%r13) l %r2, 0 (%r2, %r12) |
|
la %r8, 0 (%r2, %r7) # %r8 = &x … .L0 : # literal pool, address in %r13 .L1: .long [email protected] .L2: .long x@gotntpoff |
|
GOT [n] |
未解决的重定位 R_390_TLS_TPOFF32 x |
常规动态到局部可执行
这个把常规动态代码序列变成局部可执行代码序列的优化,与常规动态到初始可执行转换一样简单。局部可执行代码序列,直接从字常数库,载入变量到线程指针的偏移。常规动态代码序列中的函数调用,被转换为一个 nop ,字常数库常量 x@tlsgd 被 x@ntpoff 替换:
GD à LE 代码转换 |
初始重定位 符号 |
l %r6, .L1-.L0 (%r13) ear %r7, %a |
R_390_TLS_GDCALL x
R_390_TLS_GD32 x ⇓ ⇓
R_390_TLS_LE32 x |
l %r2, .L2-.L0 (%r13) bas %r14, 0 (%r6, %r13) |
|
la %r8, 0 (%r2, %r7) # %r8 = &x ... .L0 : # literal pool, address in %r13 .L1: .long [email protected] .L2: .long x@ltsgd ⇓ l %r6, .L1-.L0 (%r13) ear %r7, %a |
|
l %r2, .L2-.L0 (%r13) bc 0, 0 # nop |
|
la %r8, 0 (%r2, %r7) # %r8 = &x … .L0 : # literal pool, address in %r13 .L1: .long [email protected] .L2: .long x@ntpoff |
|
|
未解决的重定位 |
局部动态到局部可执行
局部动态到局部可执行代码转换稍微有点复杂。为了在局部动态模式中,得到一个线程局部变量的地址,需要增加 3 件事:线程指针,到代码所在模块的 TLS 块的(负的)偏移,变量在 TLS 块中的偏移。局部可执行代码只需要把线程指针加上变量到线程指针的负偏移。通过使用一个 nop 代替函数调用,使用 0 代替字常数库常量 x1@tlsldm ,以及 ntpoff 代替常量 @dtpoff ,完成转换。
LD à LE 代码转换 |
初始重定位 符号 |
l %r6, .L1-.L0 (%r13) ear %r7, %a0 |
R_390_TLS_LDCALL x1
R_390_TLS_LDM32 x1 R_390_TLS_LDO32 x1 R_390_TLS_LDO32 x2 ⇓ ⇓
R_390_TLS_LE32 x1 R_390_TLS_LE32 x2 |
l %r2, .L2-.L0 (%r13) bas %r14, 0 (%r6, %r13) la %8, 0 (%r2, %r7) |
|
l %r9, .L3-.L0 (%r13) la %r10, 0 (%r10, %r8) # %r10 = &x1 l %r9, .L4-.L0 (%r13) la %r10, 0 (%r10, %r18) # %r10 = &x2 ... .L0 : # literal pool, address in %r13 .L1: .long [email protected] .L2: .long x1@ltsldm .L3: .long x1@dtpoff .L4: .long x2@dtpoff ⇓ l %r6, .L1-.L0 (%r13) ear %r7, %a0 |
|
l %r2, .L2-.L0 (%r13) bc 0, 0 # nop la %8, 0 (%r2, %r7) |
|
l %r9, .L3-.L0 (%r13) la %r10, 0 (%r10, %r8) # %r10 = &x1 l %r9, .L4-.L0 (%r13) la %r10, 0 (%r10, %r18) # %r10 = &x2 ... .L0 : # literal pool, address in %r13 .L1: .long [email protected] .L2: .long 0 .L3: .long x1@ntpoff .L4: .long x2@ntpoff |
|
GOT [n] |
未解决的重定位 R_390_TLS_DTPMOD x1 |
初始可执行到局部可执行
初始可执行到局部可执行的转换并不提高执行的速度,但对于 3 个初始可执行版本中的 2 个,减少了一个 GOT 项。
IE à LE 代码转换 |
初始重定位 符号 |
ear %r7, %a0 |
R_390_TLS_LOAD x
R_390_TLS_GOTIE32 x ⇓ ⇓
R_390_TLS_LE32 x |
l %r8, .L1-.L0 (%r13) l %r9, 0 (%r8, %r12) |
|
la %r10, 0 (%r9, %r7) # %r10 = &x ... .L0 : # literal pool, address in %r13 .L1 : .long x@indntpoff ⇓ ear %r7, %a0 |
|
l %r8, .L1-.L0 (%r13) lr %r9, %r8 ; bcr 0, %r0 |
|
la %r10, 0 (%r9, %r7) # %r10 = &x ... .L0 : # literal pool, address in %r13 .L1 : .long x@ntpoff |
|
|
未解决的重定位 |
IE à LE 代码转换 |
初始重定位 符号 |
ear %r7, %a0 |
R_390_TLS_LOAD x
R_390_TLS_GOTIE32 x ⇓ ⇓
R_390_TLS_LE32 x |
l %r8, .L1-.L0 (%r13) l %r9, 0 (%r8, %r12) |
|
la %r10, 0 (%r9, %r7) # %r10 = &x ... .L0 : # literal pool, address in %r13 .L1 : .long x@gotntpoff ⇓ ear %r7, %a0 |
|
l %r8, .L1-.L0 (%r13) lr %r9, %r8 ; bcr 0, %r0 |
|
la %r10, 0 (%r9, %r7) # %r10 = &x ... .L0 : # literal pool, address in %r13 .L1 : .long x@ntpoff |
|
|
未解决的重定位 |
对于小 GOT ,没有 IE à LE 的代码转换,因为不存在可以保存改变后的 x@ntpoff 常量的字常数库项。对于这个情形, GOT 的一个项用于这个常量。
正如 s390 那样,同样的四种优化存在于 s390x 中。优化遵循相同的原理,但使用 64 位而不是 32 位指令。 6 字节的 bras1 指令,要么被 6 字节的 lg 载入指令替代,要么被 6 字节 brc1 0, .nop 替代。 6 字节的 lg 指令被 6 字节三合一 0 比特移位,而不是更合适但不幸只有 4 字节的 lgr ,所替代。
常规动态到初始可执行
GD à IE 代码转换 |
初始重定位 符号 |
ear %r7, %a0 sllg %r7, %r7, 32 ear %r7, %a1 |
R_390_TLS_GDCALL x
R_390_TLS_GD64 x ⇓ ⇓
R_390_TLS_LOAD x
R_390_TLS_GOTIE64 x |
lg %r2, .L1-.L0 (%r13) brasl %r14, __tls_get_offset@plt |
|
la %r8, 0 (%r2, %r7) # %r8 = &x ... .L0 : # literal pool, address in %r13 .L1: .quad x@tlsgd ⇓ ear %r7, %a0 sllg %r7, %r7, 32 ear %r7, %al |
|
lg %r2, .L1-.L0 (%r13) lg %r2, 0 (%r2, %r12) |
|
la %r8, 0 (%r2, %r7) # %r8 = &x … .L0 : # literal pool, address in %r13 .L1: .quad x@gotntpoff |
|
GOT [n] |
未解决的重定位 R_390_TLS_TPOFF64 x |
常规动态到局部可执行
GD à LE 代码转换 |
初始重定位 符号 |
ear %r7, %a0 sllg %r7, %r7, 32 ear %r7, %a1 |
R_390_TLS_GDCALL x
R_390_TLS_GD64 x ⇓ ⇓
R_390_TLS_LE64 x |
lg %r2, .L1-.L0 (%r13) brasl %r14, __tls_get_offset@plt |
|
la %r8, 0 (%r2, %r7) # %r8 = &x ... .L0 : # literal pool, address in %r13 .L1: .quad x@tlsgd ⇓ ear %r7, %a0 sllg %r7, %r7, 32 ear %r7, %al |
|
lg %r2, .L1-.L0 (%r13) brcl 0, . |
|
la %r8, 0 (%r2, %r7) # %r8 = &x … .L0 : # literal pool, address in %r13 .L1: .quad x@ntpoff |
|
|
未解决的重定位 |
局部动态到局部可执行
LD à LE 代码转换 |
初始重定位 符号 |
ear %r7, %a0 sllg %r7, %r7, 32 ear %r7, %a1 |
R_390_TLS_LDCALL x1
R_390_TLS_LDM64 x1 R_390_TLS_LDO64 x1 R_390_TLS_LDO64 x2 ⇓ ⇓
R_390_TLS_LE64 x1 R_390_TLS_LE64 x2 |
lg %r2, .L1-.L0 (%r13) brasl %r14, __tls_get_offset@plt la %r8, 0 (%r2, %r7 |
|
lg %r9, .L2-.L0 (%r13) la %r10, 0 (%r9, %r8) # %r10 = &x1 lg %r9, .L3-.L0 (%r13) la %r10, 0 (%r9, %r8) # %r10 = &x2 ... .L0 : # literal pool, address in %r13 .L1: .quad x1@tlsldm .L2: .quad x1@dtpoff .L3: .quad x2@dtpoff ⇓ ear %r7, %a0 sllg %r7, %r7, 32 ear %r7, %al |
|
lg %r2, .L1-.L0 (%r13) brcl 0, . la %r8, 0 (%r2, %r7) |
|
lg %r9, .L2-.L0 (%r13) la %r10, 0 (%r9, %r8) # %r10 = &x1 lg %r9, .L3-.L0 (%r13) la %10, 0 (%r9, %r8) # %r10 = &x2 … .L0 : # literal pool, address in %r13 .L1: .quad 0 .L2: .quad x1@ntpoff .L3: .quad x2@ntpoff |
|
|
未解决的重定位 |
初始可执行到局部可执行
IE à LE 代码转换 |
初始重定位 符号 |
ear %r7, %a0 sllg %r7, %r7, 32 ear %r7, %a1 |
R_390_TLS_LOAD x
R_390_TLS_GOTIE64 x ⇓ ⇓
R_390_TLS_LE64 x |
lg %r2, .L1-.L0 (%r13) lg %r9, 0 (%r8, %r12) |
|
la %r10, 0 (%r9, %r7) # %r10 = &x ... .L0 : # literal pool, address in %r13 .L1: .quad x@gotntpoff ⇓ ear %r7, %a0 sllg %r7, %r7, 32 ear %r7, %al |
|
lg %r8, .L1-.L0 (%r13) sllg %r9, %r8, 0 |
|
la %r10, 0 (%r9, %r7) # %r10 = &x … .L0 : # literal pool, address in %r13 .L1: .quad x@ntpoff |
|
|
未解决的重定位 |
IE à LE 代码转换 |
初始重定位 符号 |
ear %r7, %a0 sllg %r7, %r7, 32 ear %r7, %a1 |
R_390_TLS_LOAD x
R_390_TLS_GOTIE64 x ⇓ ⇓
R_390_TLS_LE64 x |
lg %r8, .L1-.L0 (%r13) lg %r9, 0 (%r8) |
|
la %r10, 0 (%r9, %r7) # %r10 = &x ... .L0 : # literal pool, address in %r13 .L1: .quad x@indntpoff ⇓ ear %r7, %a0 sllg %r7, %r7, 32 ear %r7, %al |
|
lg %r8, .L1-.L0 (%r13) sllg %r9, %r8, 0 |
|
la %r10, 0 (%r9, %r7) # %r10 = &x … .L0 : # literal pool, address in %r13 .L1: .quad x@ntpoff |
|
|
未解决的重定位 |