分析用于分析ELF文件的.bss段大小的工具的分歧

时间:2017-01-14 15:06:18

标签: gcc arm objdump nm readelf

在分析编译为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吗?

1 个答案:

答案 0 :(得分:1)

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

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 所示的符号大小完全不同:

var1

它们的大小总和是0x300002,而不是0x2100000。有两个因素促成了这一点:

  • var2未使用后,差距约为16 MiB。由于定义变量的顺序,需要实现completed.6963的对齐。部分空间由var2重用。
  • var3var4.data是别名:符号值相同,因此只有一个对象支持变量。

此外,由.bss对齐要求引起的.bss部分与 LOAD 0x000e08 0x0000000000200e08 0x0000000000200e08 0x000220 0x000220 RW 0x200000 LOAD 0x1000000 0x0000000001000000 0x0000000001000000 0x000000 0x2100000 RW 0x200000 部分的结尾之间存在很大差距。使用典型的动态加载器,这只会导致未映射的内存区域:

size

所以readelf大概是正确的,因为它不计算这个差距。

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

size / objdumpnm / public class MyVerticle extends AbstractVerticle { @Override public void start() throws Exception { HttpServer server = vertx.createHttpServer(); Router router = Router.router(vertx); OAuth2Auth authProviderGitHub = GithubAuth.create(vertx, "<CLIENT_ID>", "<CLIENT_SECRET>"); OAuth2AuthHandler oauth2 = OAuth2AuthHandler.create(authProviderGitHub, "http://localhost:8080/callback"); oauth2.setupCallback(router.route()); router.route("/protected/*").handler(oauth2); Handler<RoutingContext> requestHandler = (routingContext) -> { String paramValue = routingContext.request().getParam("param"); routingContext.response().end("PARAM: " + paramValue); }; router.get("/endpoint").handler(requestHandler); router.get("/protected/endpoint").handler(requestHandler); server.requestHandler(router::accept).listen(8080); } } 差异可能是ARM的特点;这些可能是由我的例子中没有的某些符号类型触发的。