我总是假设链接器分配了任何库的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中的某些变量对于特定的加载可执行文件是不是必需的?如果是这样,这是怎么做到的?
答案 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-headers
和p_filesz
,并从后者中扣除前者。