为什么.bss部分映射到的流程小于目标文件中报告的bss?

时间:2014-08-12 21:45:19

标签: linux memory linux-kernel shared-libraries elf

我总是假设链接器分配了任何库的bss部分并将其映射到进程中。此部分的大小取决于库报告的bss的大小。

我查看了进程的/ proc / [PID] / maps文件,并计算了加载库的bss部分的大小。

7f1f5561f000-7f1f55637000 r-xp 00000000 08:01 3018048          /usr/lib/libpthread-2.19.so
7f1f55637000-7f1f55837000 ---p 00018000 08:01 3018048          /usr/lib/libpthread-2.19.so
7f1f55837000-7f1f55838000 r--p 00018000 08:01 3018048          /usr/lib/libpthread-2.19.so
7f1f55838000-7f1f55839000 rw-p 00019000 08:01 3018048          /usr/lib/libpthread-2.19.so
7f1f55839000-7f1f5583d000 rw-p 00000000 00:00 0 
7f1f5583d000-7f1f55851000 r-xp 00000000 08:01 3017945          /usr/lib/libresolv-2.19.so
7f1f55851000-7f1f55a50000 ---p 00014000 08:01 3017945          /usr/lib/libresolv-2.19.so
7f1f55a50000-7f1f55a51000 r--p 00013000 08:01 3017945          /usr/lib/libresolv-2.19.so
7f1f55a51000-7f1f55a52000 rw-p 00014000 08:01 3017945          /usr/lib/libresolv-2.19.so
7f1f55a52000-7f1f55a54000 rw-p 00000000 00:00 0 

在这里我们可以看到libpthread的bss位于地址范围7f1f55839000-7f1f5583d000中,减去它们会给我们一个 16384字节的大小。

使用size命令或readelf,libpthread的bss部分的大小为 16848 字节。

由于虚拟地址范围需要与页面边界对齐,因此它们有所不同,但虚拟大小如何小于比elf文件报告的大小?没有足够的空间来容纳所有变量。

链接器是否能够确定bss中的某些变量对于特定的加载可执行文件是不是必需的?如果是这样,这是怎么做到的?

1 个答案:

答案 0 :(得分:2)

  

这里我们可以看到libpthread的bss位于地址范围7f1f55839000-7f1f5583d000中,减去它们给我们的大小为16384字节。

困难源于声明并非完全准确的事实。 ELF链接器和Linux加载器实际上不保证节,段和映射之间的一对一关系。结果是突出显示的映射实际上并不代表.bss所有(正如您所发现的那样)。

通常,使用默认链接描述文件的ELF链接器将创建两个LOAD段:一个R / E(用于文本和只读数据)和一个R / W(用于可写数据,包括{{1 }})。它将安排.bss显示在细分的末尾,以便在设置.bss时将其设置为关联ELF program header p_filesz以仅覆盖初始化数据更大的值,为p_memsz添加足够的空间。

当加载程序处理此段时,它将创建一个或两个映射。第一个映射将是"文件支持"来自.bss p_offset。因此,初始访问时的页面错误将保证必要的初始值。在第一个映射的最后一页中留下的任何空间将p_filesz为零(访问本身在任何初始化值中首先出现故障)。如果剩余的memset适合新填充的空间,那么加载程序需要做的就是这一切。否则,如果需要更多空间,加载器将创建一个匿名映射(由p_memsz支持)以覆盖其余部分。

因此,内存中/dev/zero的大小实际上是第二个匿名映射和" tail"的总和。紧接着的映射。可能最简单的方法是在内存中估计.bss(在链接器,加载器和内核根据各种适用的ELF,POSIX和Linux标准应用的各种对齐约束内)使用例如 .bss获取适用细分的readelf --program-headersp_filesz,并从后者中扣除前者。