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

我总是假定链接器分配了任何库的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字节。

这是有道理的,他们是不同的,因为虚拟地址范围需要排列到页面边界,但虚拟尺寸如何能够比由精灵文件报告的尺寸小? 没有足够的空间来适应所有的variables。

链接器是否能够确定某些在bss中的variables对于特定的加载可执行文件是不必要的? 如果是的话,这是怎么做的?

在这里,我们可以看到libpthread的bss位于地址范围7f1f55839000-7f1f5583d000中,并将它们相减,得到了16384字节的大小。

这个困难源于陈述并不完全准确的事实。 ELF链接器和Linux加载器实际上并不保证段,段和映射之间的一对一关系。 结果是高亮的映射实际上并不代表所有.bss (就像你发现的那样)。

通常,使用默认链接描述文件的ELF链接器将创建两个LOAD段:一个R / E(用于文本和只读数据)和一个R / W(用于可写入数据,包括.bss )。 它将安排.bss出现在段的末尾,以便它可以设置相关的ELF程序头 p_filesz ,只覆盖初始化的数据,同时将p_memsz设置为一个较大的值,为.bss添加足够的空间。

当加载器处理这个段时,它将创建一个或两个映射。 第一个映射将从p_offsetp_offset “文件支持”。 因此,初始访问时的页面错误将保证必要的初始值。 在第一个映射的最后一页中留下的任何空间都将被memset设置为零(访问本身首先出现任何初始值)。 如果剩余的p_memsz适合新填满的空间,那么装载程序需要做的就是这些。 否则,如果需要更多的空间,加载器将创建一个匿名映射(由/dev/zero )来覆盖其余部分。

因此,内存中.bss的大小实际上是第二个匿名映射和前一个映射的“尾部”的总和。 可能最简单的方法是根据各种适用的ELF,POSIX和Linux标准,对内存中的.bss进行估计(在由链接器,加载器和内核应用的各种对齐约束内),使用例如 readelf --program-headers获取适用的段的readelf --program-headersp_memsz ,并从后者中减去前者。