【问题标题】:Disagreement of tools for analyzing .bss section size of ELF file用于分析 ELF 文件的 .bss 部分大小的工具存在分歧
【发布时间】:2017-01-14 15:06:17
【问题描述】:

在分析编译为 ARM 平台的 ELF 文件的 C++ 程序的 .bss 部分时,我遇到了几种确定大小的方法。问题Tool to analyze size of ELF sections and symbol中也提到了我测试的四种方式。

然而,结果却大不相同:

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 脚本

#!/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 是不可能的吗?

【问题讨论】:

  • 你试过 llvm-nm,它与 LLVM/clang 捆绑在一起吗?也许它不会同意nm 在这方面
  • 您是测试对象文件 (ET_REL) 还是完全链接的对象(ET_EXECET_DYN)?为什么您期望将单个符号大小相加会得出正确的数字?
  • @valiano 不,llvm-nm 给出相同的结果。
  • @FlorianWeimer 这是一个 ET_EXEC。回答为什么总结各个符号大小会导致可能不正确的结果将是答案的一部分。

标签: gcc arm objdump nm readelf


【解决方案1】:

这是一个示例汇编程序文件,它生成一个可执行文件,其中显示了可能发生的一些事情:

    .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 给出这个输出:

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

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

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

但是,eu-readelf -s 显示的符号大小完全不同:

   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 是别名:符号值相同,因此只有一个对象支持变量。

此外,.data 部分的末尾和 .bss 部分之间存在很大的差距,这是由 .bss 对齐要求引起的。使用典型的动态加载器,这只会导致未映射的内存区域:

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

所以size 在不计算这个差距的情况下大概是正确的。

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

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

【讨论】:

  • 这很合理,谢谢!奇怪的是,即使那些没有明显加起来符号的解决方案(即 readelf 和 size)给出不同的结果,但无论如何这是一个很好的答案,所以我接受它。
猜你喜欢
  • 2012-07-28
  • 1970-01-01
  • 1970-01-01
  • 2011-03-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-11-29
  • 2016-06-20
相关资源
最近更新 更多