我总是假定链接器分配了任何库的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_offset
为p_offset
“文件支持”。 因此,初始访问时的页面错误将保证必要的初始值。 在第一个映射的最后一页中留下的任何空间都将被memset
设置为零(访问本身首先出现任何初始值)。 如果剩余的p_memsz
适合新填满的空间,那么装载程序需要做的就是这些。 否则,如果需要更多的空间,加载器将创建一个匿名映射(由/dev/zero
)来覆盖其余部分。
因此,内存中.bss
的大小实际上是第二个匿名映射和前一个映射的“尾部”的总和。 可能最简单的方法是根据各种适用的ELF,POSIX和Linux标准,对内存中的.bss
进行估计(在由链接器,加载器和内核应用的各种对齐约束内),使用例如 readelf --program-headers
获取适用的段的readelf --program-headers
和p_memsz
,并从后者中减去前者。