Gre*_*Cat 4 gcc elf ld static-linking thread-local-storage
我正在使用gcc版本4.8.2(Debian 4.8.2-21)在x86_64机器上的Debian 7系统上静态编译一个非常简单的hello-world单行程序:
gcc test.c -static -o test
Run Code Online (Sandbox Code Playgroud)
我得到一个可执行的ELF文件,其中包括以下部分:
[17] .tdata PROGBITS 00000000006b4000 000b4000
0000000000000020 0000000000000000 WAT 0 0 8
[18] .tbss NOBITS 00000000006b4020 000b4020
0000000000000030 0000000000000000 WAT 0 0 8
[19] .init_array INIT_ARRAY 00000000006b4020 000b4020
0000000000000010 0000000000000000 WA 0 0 8
[20] .fini_array FINI_ARRAY 00000000006b4030 000b4030
0000000000000010 0000000000000000 WA 0 0 8
[21] .jcr PROGBITS 00000000006b4040 000b4040
0000000000000008 0000000000000000 WA 0 0 8
[22] .data.rel.ro PROGBITS 00000000006b4060 000b4060
00000000000000e4 0000000000000000 WA 0 0 32
Run Code Online (Sandbox Code Playgroud)
注意,该.tbss部分分配在地址0x6b4020..0x6b4050(0x30字节),它与.init_array0x6b4020..0x6b4030(0x10字节).fini_array部分的分配相交,部分位于0x6b4030..0x6b4040(0x10字节),.jcr部分位于0x6b4040..0x6b4048 (8个字节).
注意它不包含以下几个部分相交,例如.data.rel.ro,但这可能是因为.data.rel.ro定位是32,因此它不能被放置在任何早于0x6b4060.
生成的文件运行正常,但我仍然没有完全了解它是如何工作的.从我在glibc文档中读到的内容来看,它.tbss是.bss线程本地存储的唯一部分(即分配的内存暂存空间,实际上并未映射到物理文件中).该.tbss部分是如此特殊以至于它可以与其他部分重叠吗?是.init_array,.fini_array并且.jcr是如此无用(例如,它们不再需要它们,然后TLS相关的代码运行),所以它们可以被bss覆盖?或者它是某种错误?
基本上,如果我尝试在我的应用程序中读取地址0x6b4020,我会读取和写入什么?.tbss内容或.init_array指针?为什么?
虚拟地址.tbss没有意义,因为该部分仅用作由GLIBC中的线程实现分配的TLS存储的模板.
此虚拟地址的实现方式.tbss如下所示.tbdata:默认链接描述文件:
...
.gcc_except_table : ONLY_IF_RW { *(.gcc_except_table .gcc_except_table.*) }
/* Thread Local Storage sections */
.tdata : { *(.tdata .tdata.* .gnu.linkonce.td.*) }
.tbss : { *(.tbss .tbss.* .gnu.linkonce.tb.*) *(.tcommon) }
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array))
PROVIDE_HIDDEN (__preinit_array_end = .);
}
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array))
PROVIDE_HIDDEN (__init_array_end = .);
}
...
Run Code Online (Sandbox Code Playgroud)
因此,它的虚拟地址只是前一节(.tbdata)的虚拟地址加上前一节的大小(最终有一些填充以达到所需的对齐)..init_array(或者.preinit_array如果存在的话)接下来,它的位置应该以相同的方式确定,但是.tbss已知它非常特殊,在GNU LD中给出了一个深度硬编码的处理:
/* .tbss sections effectively have zero size. */
if ((os->bfd_section->flags & SEC_HAS_CONTENTS) != 0
|| (os->bfd_section->flags & SEC_THREAD_LOCAL) == 0
|| link_info.relocatable)
dotdelta = TO_ADDR (os->bfd_section->size);
else
dotdelta = 0; // <----------------
dot += dotdelta;
Run Code Online (Sandbox Code Playgroud)
.tbss不可重定位,它SEC_THREAD_LOCAL设置了标志,并且没有contents(NOBITS),因此采用了else分支.换句话说,无论它有多大.tbss,链接器都不会提前跟随它的部分的位置(也称为"点").
另请注意,它.tbss位于不可加载的ELF段中:
Program Headers:
Type Offset VirtAddr PhysAddr
FileSiz MemSiz Flags Align
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000b1f24 0x00000000000b1f24 R E 200000
LOAD 0x00000000000b2000 0x00000000006b2000 0x00000000006b2000
0x0000000000002288 0x00000000000174d8 RW 200000
NOTE 0x0000000000000158 0x0000000000400158 0x0000000000400158
0x0000000000000044 0x0000000000000044 R 4
TLS 0x00000000000b2000 0x00000000006b2000 0x00000000006b2000 <---+
0x0000000000000020 0x0000000000000060 R 8 |
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 |
0x0000000000000000 0x0000000000000000 RW 8 |
|
Section to Segment mapping: |
Segment Sections... |
00 .note.ABI-tag ... |
01 .tdata .ctors ... |
02 .note.ABI-tag ... |
03 .tdata .tbss <---------------------------------------------------+
04
Run Code Online (Sandbox Code Playgroud)