gcc / ld:静态链接的ELF二进制文件中的重叠部分(.tbss,.init_array) [英] gcc / ld: overlapping sections (.tbss, .init_array) in statically-linked ELF binary
问题描述
我在x86_64机器上使用gcc 4.8.2版(Debian 4.8.2-21)在Debian 7系统上静态编译一个非常简单的hello-world单行程:
gcc test.c -static -o test
和我得到一个可执行的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 0000000000 000000 WA 0 0 8
[22] .data.rel.ro PROGBITS 00000000006b4060 000b4060
00000000000000e4 0000000000000000 WA 0 0 32
请注意,地址0x6b4020..0x6b4050(0x30字节)分配了 请注意 与以下各节相交,例如 生成的文件运行正常,但我仍然不完全知道它作品。从我在glibc文档中读到的内容来看, 基本上,如果我尝试在应用程序中读取地址0x6b4020,我该如何读写? 这个虚拟地址到位的方式是 因此它的虚拟地址就是虚拟地址( 请注意, I'm compiling a very simple hello-world one-liner statically on Debian 7 system on x86_64 machine with gcc version 4.8.2 (Debian 4.8.2-21): and I get an executable ELF file that includes the following sections: Note that Note it does not intersect with the following sections, for example, The resulting file runs ok, but I still don't exactly get how it works. From what I read in glibc documentation, Basically, what do I get to read and write if I'll try to read address 0x6b4020 in my application? The virtual address of The way this virtual address comes into place is that therefore its virtual address is simply the virtual address of the preceding section ( Note also that
这篇关于gcc / ld:静态链接的ELF二进制文件中的重叠部分(.tbss,.init_array)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋! .tbss
部分,并与<$ c $ 0x6b4030..0x6b4040(0x10字节)处的0x6b4020..0x6b4030(0x10字节), .fini_array
部分的
.data.rel.ro
,但这可能是因为 .data.rel.ro
alignment是32,因此它不能放在任何早于0x6b4060的位置。
.tbss
是线程本地存储的简单 .bss
部分(即分配的内存暂存空间,没有真正映射到物理文件中)。是不是 .tbss
部分非常特殊以至于可以与其他部分重叠?是 .init_array
, .fini_array
和 .jcr
是如此没用(例如,它们不再需要,那么TLS相关的代码运行),所以它们可以被bss覆盖?或者它是某种错误?
.tbss
内容或 .init_array
指针?为什么? .tbss
的虚拟地址没有意义,因为部分仅用作由GLIBC中的线程实现分配的TLS存储的模板。
.tbss
在默认链接描述文件中遵循 .tbdata
: $ b < code $ ...
.gcc_except_table:ONLY_IF_RW {*(.gcc_except_table .gcc_except_table。*)}
/ *线程本地存储部分* /
.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 =。);
}
...
.tbdata
)加上前一部分的大小(最后用一些填充以达到所需的对齐方式)。 .init_array
(或 .preinit_array
如果存在的话)接下来,它的位置应该以相同的方式确定,但<$已知c $ c> .tbss 非常特别,它在GNU LD内部被赋予了一个深刻的硬编码处理:
/ * .tbss部分实际上具有零大小。 ((os-> bfd_section->标志& SEC_HAS_CONTENTS)!= 0
||(os-> bfd_section->标志& SEC_THREAD_LOCAL)== 0
|| link_info.relocatable)
dotdelta = TO_ADDR(os-> bfd_section-> size);
else
dotdelta = 0; //< ----------------
dot + = dotdelta;
.tbss
不可重定位, SEC_THREAD_LOCAL
标记集,它没有内容( NOBITS
),因此 else采取
分支。换句话说,无论 .tbss
的大小如何,链接器都不会提前跟随它的部分的位置(也称为点)。
.tbss
位于不可载入的ELF分段中:
程序标题:
类型偏移量VirtAddr PhysAddr
FileSiz MemSiz标志对齐
LOAD 0x0000000000000000 0x0000000000400000 0x0000000000400000
0x00000000000b1f24 0x00000000000b1f24 RE 200000
LOAD 0x00000000000b2000 0x00000000006b2000 0x00000000006b2000
0x0000000000002288 0x00000000000174d8 RW 200000
注意0x0000000000000158 0x0000000000400158 0x0000000000400158
0x0000000000000044 0x0000000000000044 R 4
TLS 0x00000000000b2000 0x00000000006b2000 0x00000000006b2000 < - +
0x0000000000000020 0x0000000000000060 R 8 |
GNU_STACK 0x0000000000000000 0x0000000000000000 0x0000000000000000 |
0x0000000000000000 0x0000000000000000 RW 8 |
|
部分到段映射:|
细分市场... |
00 .note.ABI-tag ... |
01 .tdata .ctors ... |
02 .note.ABI-tag ... |
03 .tdata .tbss< --------------------------------------- ------------ +
04
gcc test.c -static -o test
[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
.tbss
section is allocated at addresses 0x6b4020..0x6b4050 (0x30 bytes) and it intersects with allocation of .init_array
section at 0x6b4020..0x6b4030 (0x10 bytes), .fini_array
section at 0x6b4030..0x6b4040 (0x10 bytes) and with .jcr
section at 0x6b4040..0x6b4048 (8 bytes)..data.rel.ro
, but that's probably because .data.rel.ro
alignment is 32 and thus it can't be placed any earlier than 0x6b4060..tbss
is a just .bss
section for thread local storage (i.e. allocated memory scratch space, not really mapped in physical file). Is it that .tbss
section is so special that it can overlap other sections? Are .init_array
, .fini_array
and .jcr
are so useless (for example, they are not needed anymore then TLS-related code runs), so they can be overwritten by bss? Or is it some sort of a bug?.tbss
contents or .init_array
pointers? Why?.tbss
is meaningless as that section only serves as a template for the TLS storage as allocated by the threading implementation in GLIBC..tbss
follows .tbdata
in the default linker script:...
.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 = .);
}
...
.tbdata
) plus the size of the preceding section (eventually with some padding in order to reach the desired alignment). .init_array
(or .preinit_array
if present) comes next and its location should be determined the same way, but .tbss
is known to be so very special, that it is given a deeply hard-coded treatment inside 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;
.tbss
is not relocatable, it has the SEC_THREAD_LOCAL
flag set, and it does not have contents (NOBITS
), therefore the else
branch is taken. In other words, no matter how large the .tbss
is, the linker does not advance the location of the section that follows it (also know as "the dot")..tbss
sits in a non-loadable ELF segment: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