关于gcc:用于分析ELF文件.bss节大小的工具的分歧

Disagreement of tools for analyzing .bss section size of ELF file

在分析编译为ARM平台的ELF文件的C程序的.bss部分时,我遇到了几种确定大小的方法。问题工具中还提到了我测试的四种方法,用于分析ELF部分和符号的大小。

但是,结果却大不相同:

1
2
3
4
bss size according to nm:       35380
bss size according to readelf:  37632
bss size according to size:     37888
bss size according to objdump:  37594

这可能是什么原因?

用于生成输出的Python脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#!/usr/bin/env python
import re
import subprocess
import sys

fname = sys.argv[1]

# nm
output = subprocess.check_output(['arm-none-eabi-nm','-l','-S','-C',fname])
size = 0
for line in output.splitlines():
    m = re.search('[0-9a-f]* ([0-9a-f]*) ([a-zA-Z]) ([^/]*)\\s*([^\\s]*)',line)
    if m:
        stype = m.group(2).strip()
        if stype in ['B','b']:
            size += int(m.group(1),16)

print"bss size according to nm: \\t%i" % size

# readelf
output = subprocess.check_output(['arm-none-eabi-readelf','-S',fname])
for line in output.splitlines():
    m = re.search('bss\\s+[A-Z]+\\s+[0-9a-f]+ [0-9a-f]+ ([0-9a-f]+)',line)
    if m:
        print"bss size according to readelf: \\t%i" % int(m.group(1),16)
        break

# size
output = subprocess.check_output(['arm-none-eabi-size',fname])
for line in output.splitlines():
    m = re.search('[0-9]+\\s+[0-9]+\\s+([0-9]+)',line)
    if m:
        print"bss size according to size: \\t%i" % int(m.group(1))
        break

# objdump
output = subprocess.check_output(['arm-none-eabi-objdump','-C','-t','-j','.bss',fname])
size = 0
for line in output.splitlines():
    m = re.search('bss\\s+([0-9a-f]*)\\s+',line)
    if m:
        size += int(m.group(1),16)

print"bss size according to objdump: \\t%i" % size

编辑:我发现的一件事是,nm将函数内部的静态变量(正确)分类为弱(V),尽管它们可能是.bss的一部分。但是,并非所有分类为V的节都属于.bss,因此我不能仅将所有V节添加到大小中。那么使用nm不可能完成此任务吗?


这是一个示例汇编器文件,该文件生成一个可执行文件,该可执行文件显示了可能发生的一些事情:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
    .section .bss
    .globl var1
    .size var1, 1
var1:
    .skip 1

    .align 16777216
    .globl var2
    .size var2, 1048576
    .globl var3
    .size var3, 1048576
    .globl var4
    .size var4, 1048576
var2:
var3:
var4:
    .skip 1048576

    .text

    .globl main
main:
    xor %eax, %eax
    ret

size -x给出以下输出:

1
2
   text    data     bss     dec     hex filename
  0x5c9   0x220 0x2100000   34605033    21007e9 a.out

eu-readelf -S显示基本相同的信息:

1
[25] .bss                 NOBITS       0000000001000000 01000000 02100000  0 WA     0   0 16777216

但是,符号大小(如eu-readelf -S所示)却大不相同:

1
2
3
4
5
   32: 0000000001000001       1 OBJECT  LOCAL  DEFAULT       25 completed.6963
   48: 0000000003000000 1048576 NOTYPE  GLOBAL DEFAULT       25 var2
   49: 0000000003000000 1048576 NOTYPE  GLOBAL DEFAULT       25 var4
   59: 0000000002000000       1 NOTYPE  GLOBAL DEFAULT       25 var1
   61: 0000000003000000 1048576 NOTYPE  GLOBAL DEFAULT       25 var3

它们的大小之和为0x300002,而不是0x2100000。有两个因素对此造成影响:

  • var1之后有大约16 MiB的间隙未使用。由于定义变量的顺序,需要实现var2的对齐。 completed.6963重用了一些空间。
  • var2var3var4是别名:符号值相同,因此只有单个对象支持该变量。

此外,由于.bss对齐要求,导致.data部分的末端与.bss部分之间存在较大的差距。对于典型的动态加载器,这只会导致未映射的内存区域:

1
2
  LOAD           0x000e08 0x0000000000200e08 0x0000000000200e08 0x000220 0x000220 RW  0x200000
  LOAD           0x1000000 0x0000000001000000 0x0000000001000000 0x000000 0x2100000 RW  0x200000

因此size在不计算此间隙时大概是正确的。

此示例中的数字肯定过大,但是即使使用常规二进制文件,这些影响也可见,但程度较小。

readelf / sizeobjdump / nm的差异可能是ARM的特性;这些可能是由我的示例中不存在的某些符号类型触发的。