ELF对线程局部储存的处理(4)
来源:互联网 发布:mac popo登录408 编辑:程序博客网 时间:2024/06/06 07:02
4.3.初始可执行TLS模式
如果已知被访问的变量出现在程序启动时的一个模块中,并且如果程序选择使用静态访问模式,可以使用一个限制更多的优化。后一个条件意味着,所产生的代码不会使用函数__tls_get_addr,这又意味着,推迟为以这个方式访问的TLS块分配内存,是不可能的。但对于动态加载的模块,推迟分配仍然是可能的。
这个优化背后的想法是,在动态链接器载入所有被执行映像(及其它一些,像由LD_PRELOAD命名的,映像)直接或间接引用的模块后,每个在这些模块的TLS块中的变量都有到TCB的固定偏移,因为要求连续分配用于初始化载入模块的内存。通过在3.4节描述的特定于架构的tlsoffsetm的公式(其中m是在其中找到这个变量的模块的ID),然后加上这个变量在TLS块中的偏移,来计算这些偏移。
这个优化的后果是,对于每个变量,在其GOT项中有一个运行时重定位,它指示动态链接器计算到TCB的偏移。这里不需要计算模块ID。因此,从常规动态模式算起,运行时重定位的数目少了一半。
在下面讨论中的代码序列实现了对一个变量x的简单访问:
extern __thread int x;
&x;
4.3.1.IA-64初始可执行TLS模式
初始可执行模式要求代码序列,在GOT的某个位置,获取由动态链接器放入的,到TCB的相对偏移。并把这个值加上线程指针。它非常短小精悍。
初始可执行模式代码序列
初始重定位 符号
0x00 addl t1=@ltoff (@tprel (x)), gp
;;
0x10 ld8 t2=[t1]
0x20 add loc0=t2, tp
R_IA_64_LTOFF_TPRELD22 x
R_IA_64_DTPREL22 x
GOT [n]
未解决的重定位
R_IA_64_TPREL64LSB x
表达式@ltoff (@tprel (x))指示链接器创建一个R_IA_64_LTOFF_TPREL22重定位,这进而要求链接器构建一个带有一个R_IA_64_TPREL64LSB重定位的GOT项。这个重定位在程序启动时由动态链接器处理,产生该变量相对于TCB块(由tp寄存器指向)的偏移。这个偏移值仅需要由0x10处的指令载入ld8,然后加上tp寄存器的值,在loc0寄存器中得到最终的地址。
为了改进行为,这些指令可以和其它指令任意混合(theinstructions can be freely mixed with other to enhance policy)。尤其是tp寄存器的处理可以被优化。
4.3.2.IA-32初始可执行TLS模式
IA-32用于初始可执行模式的代码是简单而快的。唯一的问题是定位TCB块。到目前为止所支持的,平台所使用的机制,是使用%gs段寄存器。通过这个段寄存器,访问偏移为0的内存,能够载入TCB的地址。同平时一样,我们先处理Sun的版本。
初始可执行模式代码序列
初始重定位 符号
0x00 movl x@tpoff (%ebx), %edx
0x06 movl %gs:0, %eax
0x0c subl %edx, %eax
R_386_TLS_IE_32 x
GOT [n]
未解决的重定位
R_386_TLS_TPOFF32 x
汇编器为x@tpoff (%ebx)表达式产生一个R_386_TLS_IE_32重定位,它要求链接器产生一个带有R_386_TLS_TPOFF32重定位的GOT项。然后这个GOT项的偏移被用在这个指令中。重定位R_386_TLS_TPOFF32,在程序启动期间,由动态链接器在这个期间载入的模块中查找符号x,得到处理。得到的偏移被写入该GOT项,随后被0x00处的指令载入%edx寄存器。
在0x06处的movl指令把当前线程的线程指针载入%eax寄存器。这一步最终要被调整为平台访问线程指针所采用的方法。
最后,subl指令计算出最终的地址。注意到必须从线程指针减去这个偏移。在IA-32所采用的版本II的线程局部储存数据结构中,TLS块位于TCB之前。
这个代码序列仅要求三条指令,并像常规动态模式那样占据14个字节。这可以做得更好,正如GNU版本所显示的那样。这里有两个不同的GNU版本,一个用于位置无关代码,它使用GOT指针;一个不使用GOT指针。位置无关版本:
初始可执行模式代码序列,II
初始重定位 符号
0x00 movl %gs:0, %eax
0x06 addl x@gotntpoff (%ebx), %eax
R_386_TLS_GOTIE x
GOT [n]
未解决的重定位
R_386_TLS_TPOFF x
这个代码序列的行为基本相同,除了加上而不是减去GOT的值,它把从GOT载入与算术运算合并到一条指令中。没有GOT指针的版本是:
初始可执行模式代码序列,III
初始重定位 符号
0x00 movl %gs:0, %eax
0x06 addl x@indntpoff (%ebx), %eax
R_386_TLS_IE x
GOT [n]
未解决的重定位
R_386_TLS_TPOFF x
这个代码序列形成相同的动态重定位,不过在指令中,它解析到GOT槽的绝对地址,而不是到GOT头的相对地址。
GNU版本使用一个,计算变量在TLS块中的负偏移,而不是正偏移的重定位。这是一个重大的改进,偏移可以直接嵌入在一个内存地址中(参见下面)。
那么在Sun模式下,加载x的内容(而不是它的地址),可以使用以下的代码序列:
初始可执行模式代码序列,IV
初始重定位 符号
0x00 movl x@tpoff (%ebx), %edx
0x06 movl %gs:0, %eax
0x0c subl %edx, %eax
0x0emovl (%eax), %eax
R_386_TLS_IE_32 x
GOT [n]
未解决的重定位
R_386_TLS_TPOFF32 x
这是前面的代码序列在末尾加上一个额外载入。相比较下,GNU序列不需要那么长。位置无关版本看起来就这样:
初始可执行模式代码序列,V
初始重定位 符号
0x00 movl x@gotntpoff (%ebx), %eax
0x06 movl %gs: (%eax), %eax
R_386_TLS_GOTIE x
GOT [n]
未解决的重定位
R_386_TLS_TPOFF x
版本II在TCB之前就是静态TLS,因而通过%gs寄存器指向的内存位置负的偏移,直接访问它。对于非位置无关代码,代码序列就像:
初始可执行模式代码序列,VI
初始重定位 符号
0x00 movl x@indntpoff, %ecx
0x06 movl %gs: (%ecx), %eax
R_386_TLS_IE x
GOT [n]
未解决的重定位
R_386_TLS_TPOFF x
在这个最后的序列中,如果使用%eax寄存器,而不是上面的%ecx寄存器,第一条指令可能是5或6字节长。
4.3.3.SPARC初始可执行TLS模式
这里给出的SPARC初始可执行代码序列,依赖于在%l7寄存器中的GOT指针及在%g7中的线程指针。如果这些寄存器可用的话,代码序列是简单的。对于32及64位平台,我们有两个不同的版本,因为我们要从内存载入一个GOT项,而这个项在32及64位机器有不同的大小。
初始可执行模式代码序列
初始重定位 符号
0x00 sethi %hi (@tpoff (x)), %o0
0x04 or %o0, %lo (@tpoff (x)), %o0
0x08 ld [%l7 + %o0], %o0
0x0c add %g7, %o0, %o0
R_SPARC_TLS_IE_HI22 x
R_SPARC_TLS_IE_LO10 x
R_SPARC_TLS_IE_LD x
R_SPARC_TLS_IE_ADD x
GOT [n]
未解决的重定位
R_SPARC_TLS_TPOFF32 x
代码把GOT项的偏移常量载入%o0寄存器。操作符@tpoff(x)构建重定位R_SPARC_TLS_IE_HI22及R_SPARC_TLS_IE_LO10,它们指示链接器分配GOT项,并附加重定位R_SPARC_TLS_TPOFF32。然后指令ld载入这个GOT项。为了使链接器能识别这个指令,加入了一个R_SPARC_TLS_IE_LD重定位。最后指令add计算x的地址。这个指令具有R_SPARC_TLS_IE_ADD重定位标记。注意到由动态链接器产生的偏移,希望是负的,使得它可以与线程指针相加。
初始可执行模式代码序列
初始重定位 符号
0x00 sethi %hi (@tpoff (x)), %o0
0x04 or %o0, %lo (@tpoff (x)), %o0
0x08 ld [%l7 + %o0], %o0
0x0c add %g7, %o0, %o0
R_SPARC_TLS_IE_HI22 x
R_SPARC_TLS_IE_LO10 x
R_SPARC_TLS_IE_LDX x
R_SPARC_TLS_IE_ADD x
GOT [n]
未解决的重定位
R_SPARC_TLS_TPOFF64 x
64位版本基本上一样,除了GOT项按64位计算及载入。标记ld指令的重定位也相应地不同。
4.3.4.SH初始可执行TLS模式
这个初始可执行代码序列没有新奇之处。它是一个RISC机器,在有限的偏移及没有直接可用的线程指针的限制下,能产生的最简单的代码。
初始可执行模式代码序列
初始重定位 符号
0x00 mov.l lf, r0
0x02 stc gbr, r1
0x04 mov.l @(r0, r12), r0
0x06 bar 2f
0x08 add r1, r0
.align 2
1: .long x@gottpoff
2: …
R_SH_TLS_IE_32 x
GOT [n]
未解决的重定位
R_SH_TLS_TPOFF32 x
首先载入的是x相对于线程指针的偏移。像往常一样,这是间接完成的。标记1:的字仅具有与代码序列关联的重定位。链接器将填入GOT项的偏移,这个项将包含静态TLS块中变量的偏移。这个GOT项将由动态链接器填写。在0x04的指令把该GOT项的值载入r0寄存器,并向它加入线程寄存器的值。对于一个加法,线程寄存器的值不是直接可用的,因此它首先被移入一个正式的寄存器。
对于这个初始可执行代码序列,再一次的,它们不能改动。链接器必须找到使用由x@gottpoff所产生的重定位的指令。
4.3.5.Alpha初始可执行TLS模式
这个初始可执行模式要求线程指针从PCB载入一个通用寄存器。并可预计这应该在函数的一开始就完成,之后这个值被重用。但为了完整性,在例子的序列中包括了PALcall。
初始可执行模式代码序列
初始重定位 符号
0x00 call_pal PAL_rduniq
0x04 mov $0, $tp
...
0x10 ldq $1, x ($gp) ¡gottprel
0x14 addq $tp, $1, $1
R_ALPHA_GOTTPREL x
GOT [n]
未解决的重定位
R_ALPHA_TPREL64 x
重定位指示符!gottprel指导链接器构建一个包含R_ALPHA_TPREL64重定位的GOT项。这个重定位,在程序启动期间,由动态链接器处理,为这个变量产生相对于TCB块的偏移。这个偏移仅需要被载入,并加上线程指针的值,来得到绝对地址。
4.3.6.x86-64初始可执行TLS模式
x86-64初始可执行模式的代码使用%fs段寄存器来定位TCB。使用这个段寄存器,访问偏移为0处的内存,能够载入TCB的地址。
初始可执行模式代码序列
初始重定位 符号
0x00 movq %fs:0, %rax
0x09 addq x@gottpoff (%rip), %rax
R_X86_64_GOTTPOFF x
GOT [n]
未解决的重定位
R_X86_64_TPOFF64 x
汇编器根据表达式x@gottpoff (%rip),为符号x产生一个R_X86_64_GOTTPOFF重定位,它要求链接器构建一个具有R_X86_64_TPOFF64重定位的GOT项。接着该GOT项相对于该指令结尾的偏移被用在这个指令里。重定位R_X86_64_TPOFF64,在程序启动期间,由动态链接器在那时载入的模块中查找符号x,得到处理。这个偏移被写入这个GOT项,并随后为addq指令载入。
为了载入x的内容(而不是其地址),可用一个同样长的序列:
初始可执行模式代码序列,II
初始重定位 符号
0x00 movq x@gottpoff (%rip), %rax
0x09 movq %fs: (%rax), %rax
R_X86_64_GOTTPOFF x
GOT [n]
未解决的重定位
R_X86_64_TPOFF64 x
4.3.7.s390初始可执行TLS模式
用于这个初始可执行模式的代码小而快。代码需要从GOT得到相对于线程指针的偏移,并加上线程指针。这里有3个不同的版本。使用小GOT(-fpic)的位置无关的版本是:
初始可执行模式代码序列
初始重定位 符号
ear %r7, %a0
R_390_TLS_GOTIE12 x
l %r9, x@gotntpoff (%r12)
la %r10, 0 (%r9, %r7) # %r10 = &x
GOT [n]
未解决的重定位
R_390_TLS_TPOFF32 x
为表达式x@gotntpoff所创建的重定位R_390_TLS_GOTIE12,使得链接器产生一个具有R_390_TLS_TPOFF重定位的GOT项。链接器用,从GOT头到这个生成的GOT项的12位偏移,来代替x@gotntpoff。重定位R_390_TLS_TPOFF,在程序启动期间,由动态链接器处理。
使用大GOT(-fPIC)的位置无关版本是:
初始可执行模式代码序列
初始重定位 符号
ear %r7, %a0
R_390_TLS_LOAD x
R_390_TLS_GOTIE32 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
GOT [n]
未解决的重定位
R_390_TLS_TPOFF32 x
重定位R_390_TLS_GOTIE32与R_390_TLS_GOTIE12的作用相同,区别在于链接器使用32位而不是12位GOT偏移来替代x@gotntpoff。
没有GOT指针的版本是:
初始可执行模式代码序列
初始重定位 符号
ear %r7, %a0
R_390_TLS_LOAD x
R_390_TLS_IE32 x
l %r8, .L1-.L0 (%r13)
l %r9, 0 (%r8)
la %r10, 0 (%r9, %r7) # %r10 = &x
...
.L0 : # literal pool, address in %r13
.L1: .long x@indntpoff
GOT [n]
未解决的重定位
R_390_TLS_TPOFF32 x
重定位R_390_TLS_IE32指示链接器,像为R_390_TLS_GOTIE{12, 32}那样,构建相同的GOT项,不过链接器使用所创建GOT项的绝对地址来替代x@indntpoff表达式。这使得没有GOT指针的版本,对于位置无关代码是不充分可用的。
4.3.8.s390x初始可执行TLS模式
s390x的初始可执行模式与s390的以相似的方式工作。使用小GOT(-fpic)的位置无关版本是:
初始可执行模式代码序列
初始重定位 符号
ear %r7, %a0
sllg %r7, %r7, 32
ear %r7, %a1
R_390_TLS_GOTIE12 x
lg %r9, x@gotntpoff (%r12)
la %r10, 0 (%r9, %r7) # %r10 = &x
GOT [n]
未解决的重定位
R_390_TLS_TPOFF32 x
使用大GOT(-fPIC)的位置无关版本是:
初始可执行模式代码序列
初始重定位 符号
ear %r7, %a0
sllg %r7, %r7, 32
ear %r7, %a1
R_390_TLS_LOAD x
R_390_TLS_GOTIE64 x
lg %r8, .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
GOT [n]
未解决的重定位
R_390_TLS_TPOFF64 x
对于R_390_TLS_GOTIE64,链接器使用64位GOT偏移替代x@gotntpoff。不使用GOT指针的版本是:
初始可执行模式代码序列
初始重定位 符号
ear %r7, %a0
sllg %r7, %r7, 32
ear %r7, %a1
R_390_TLS_IEENT x
R_390_TLS_LOAD x
larl %r8, x@indntpoff
lg %r9, 0 (%r8)
la %r10, 0 (%r9, %r7) # %r10 = &x
GOT [n]
未解决的重定位
R_390_TLS_TPOFF32 x
重定位R_390_TLS_IEENT使得x@indntpoff被从larl指令到这个GOT项的相对偏移所替代。因为这个指令是相对于PC的(pc relative),这个不使用GOT指针的版本可以同样用在位置无关代码中。
- ELF对线程局部储存的处理(4)
- ELF对线程局部储存的处理(1)
- ELF对线程局部储存的处理(2)
- ELF对线程局部储存的处理(3)
- ELF对线程局部储存的处理(5)
- ELF对线程局部储存的处理(6)
- ELF对线程局部储存的处理(7)
- ELF对线程局部储存的处理(8)
- ELF对线程局部储存的处理(1) __thread
- Windows核心编程笔记(十七) 线程局部储存
- Bfd对elf文件头的处理
- TLS 局部线程储存 一个演示小例子
- 局部的变量与成员变量对线程的影响
- 消息线程,对MFC消息机制的局部模拟
- java实现线程安全的栈(链式储存)
- 线程局部存储(TLS)的使用
- java服务对线程并发的处理
- 线程局部存储,Part 5:加载器对__declspec(thread)变量的支持(进程初始化阶段)
- ELF对线程局部储存的处理(1)
- Oracle 知识备份(二)2011年4月4号
- ELF对线程局部储存的处理(2)
- Foxmail中设置Gmail教程
- ELF对线程局部储存的处理(3)
- ELF对线程局部储存的处理(4)
- Oracle 知识备份(三)2011年4月4号
- ELF对线程局部储存的处理(5)
- 品牌战略
- Ubuntu 10.10 下面禁用触摸板
- JSP Servlet 小记
- 如何在各种字符串类型之间进行转换(VS2010)
- FastWei is born...
- 详细解析Java中抽象类和接口的区别