gcc / ld:静态链接的ELF二进制文件中的重叠部分(.tbss,.init_array) [英] gcc / ld: overlapping sections (.tbss, .init_array) in statically-linked ELF binary

查看:286
本文介绍了gcc / ld:静态链接的ELF二进制文件中的重叠部分(.tbss,.init_array)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在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字节)分配了 .tbss 部分,并与<$ c $ 0x6b4030..0x6b4040(0x10字节)处的0x6b4020..0x6b4030(0x10字节), .fini_array 部分的 .init_array code> .jcr 部分在0x6b4040..0x6b4048(8字节)。

请注意 与以下各节相交,例如 .data.rel.ro ,但这可能是因为 .data.rel.ro alignment是32,因此它不能放在任何早于0x6b4060的位置。



生成的文件运行正常,但我仍然不完全知道它作品。从我在glibc文档中读到的内容来看, .tbss 是线程本地存储的简单 .bss 部分(即分配的内存暂存空间,没有真正映射到物理文件中)。是不是 .tbss 部分非常特殊以至于可以与其他部分重叠?是 .init_array .fini_array .jcr 是如此没用(例如,它们不再需要,那么TLS相关的代码运行),所以它们可以被bss覆盖?或者它是某种错误?



基本上,如果我尝试在应用程序中读取地址0x6b4020,我该如何读写? .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


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):

gcc test.c -static -o test

and I get an executable ELF file that includes the following sections:

[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

Note that .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).

Note it does not intersect with the following sections, for example, .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.

The resulting file runs ok, but I still don't exactly get how it works. From what I read in glibc documentation, .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?

Basically, what do I get to read and write if I'll try to read address 0x6b4020 in my application? .tbss contents or .init_array pointers? Why?

解决方案

The virtual address of .tbss is meaningless as that section only serves as a template for the TLS storage as allocated by the threading implementation in GLIBC.

The way this virtual address comes into place is that .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 = .);
}
...

therefore its virtual address is simply the virtual address of the preceding section (.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").

Note also that .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

这篇关于gcc / ld:静态链接的ELF二进制文件中的重叠部分(.tbss,.init_array)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆