【问题标题】:How to analyze the size of compiled functions (and their dependencies) in a binary executable?如何分析二进制可执行文件中编译函数(及其依赖项)的大小?
【发布时间】:2021-11-26 21:43:34
【问题描述】:

是否有工具可以分析二进制可执行文件中编译函数的大小及其依赖关系

在我的例子中,程序是用 C++ 编写的,但我有兴趣分析的大多数依赖项都来自 C 库。平台是 Linux。

我的目标是减小程序的静态链接版本的大小。我正在考虑删除对库函数的调用(并实现我自己的替换),但在我开始做这项工作之前,我想知道哪些库函数造成的“膨胀”最多。

例如,考虑以下程序test.c

int foo (int n)                 { return n+1; }
int main(int argc, char** argv) { return foo(argc)+1; }

当然,这是一个没有任何库的简单示例,但我将其用于说明目的。

我用gcc test.c编译这个。

objdump -d a.out 的输出包括以下内容:

0000000000001129 <foo>:
    1129:       f3 0f 1e fa             endbr64 
    112d:       55                      push   %rbp
    112e:       48 89 e5                mov    %rsp,%rbp
    1131:       89 7d fc                mov    %edi,-0x4(%rbp)
    1134:       8b 45 fc                mov    -0x4(%rbp),%eax
    1137:       83 c0 01                add    $0x1,%eax
    113a:       5d                      pop    %rbp
    113b:       c3                      retq   

000000000000113c <main>:
    113c:       f3 0f 1e fa             endbr64 
    1140:       55                      push   %rbp
    1141:       48 89 e5                mov    %rsp,%rbp
    1144:       48 83 ec 10             sub    $0x10,%rsp
    1148:       89 7d fc                mov    %edi,-0x4(%rbp)
    114b:       48 89 75 f0             mov    %rsi,-0x10(%rbp)
    114f:       8b 45 fc                mov    -0x4(%rbp),%eax
    1152:       89 c7                   mov    %eax,%edi
    1154:       e8 d0 ff ff ff          callq  1129 <foo>
    1159:       83 c0 01                add    $0x1,%eax
    115c:       c9                      leaveq 
    115d:       c3                      retq   
    115e:       66 90                   xchg   %ax,%ax

我们可以在地址1154 看到main 调用foo

所以我正在寻找能够告诉我以下信息的工具:

  • main 的大小为 36 字节。
  • foo 的大小为 19 个字节。
  • main 及其依赖项的大小为 55 字节。(这是棘手的数字要计算,因为该工具需要执行递归下降搜索来找到所有main所依赖的函数。)
  • main 依赖于以下函数:foo

如果该工具还告诉我函数使用的任何静态数据结构的大小,那将是一个额外的好处。

我发现了以下相关的 Stack Overflow 问题,但似乎没有一个答案提到可以进行我想要的依赖关系分析的工具。

Measure static memory usage for C++ ported to embedded platform
Analyzing an ELF binary to minimize its size
Tool to analyze size of ELF sections and symbol

puncover 看起来不错,但似乎缺少我想要的依赖关系分析。

【问题讨论】:

    标签: c dependencies size analysis objdump


    【解决方案1】:

    由于您似乎使用的是 GNU 工具链,因此一种半简单的方法是在编译时使用选项 -ffunction-sections,在链接程序时使用选项 -M"

    编译器选项-ffunction-sections 告诉编译器将每个函数拆分为自己的.text 部分,链接器选项-M 告诉编译器将内存映射输出到控制台。内存映射表将包含每个对象的大小。

    例如,我编译了一个具有两个函数的程序,main()test() 位于单个源文件 main.c 中。输出包含:

    这里每个函数的大小(以字节为单位)用红色下划线标记。

    请注意,此数据将隐藏在程序中定义的所有其他符号中,包括与程序链接的标准库和其他库,因此您需要搜索函数名称。在图中,我只显示了我定义的两个函数的表格内容。

    另请注意,这可能不适用于static 函数,尤其是在启用优化的情况下。

    要识别依赖关系,您可以使用 cflow (https://www.gnu.org/software/cflow/manual/cflow.html) 之类的程序生成调用图并通过调用图识别依赖关系。

    或者,您可以通过创建一个调用您要分析的函数的main() 手动执行此操作,使用选项-ffunction-sections 进行编译,然后使用选项--gc-sections-M 链接。映射文件将分配使用的函数并列出未使用的函数。

    在任何一种方法中,您都可能需要找到自己的方法来使用数据来识别具有依赖性的大小。

    【讨论】:

    • 但我还希望分析工具跟踪函数树并告诉我函数的总大小加上它调用的所有(子)函数。 (请注意我的问题:“main 及其依赖项的大小为 55 个字节。”)您的方法似乎只告诉我每个函数本身的大小。但我可以(相当)轻松地从我的问题中的objdump 输出中得出这一点。还是我错过了什么?谢谢!
    【解决方案2】:

    是否有工具可以分析二进制可执行文件中编译函数的大小及其依赖关系?

    是的,链接器 ld(1) 可以。

    您可以使用链接器附带的工具集(主要是nm(1)objdump(1))以文本形式查看目标文件的内容。

    虽然您可以在可执行文件中执行此操作,但通常最好使用目标文件 .o

    【讨论】:

    • 那么我使用什么命令行参数来使ld 打印出(例如)printf 的大小加上printf 的所有依赖项的大小?我想要一个工具来总结所有尺寸并给我一个单个数字。关于分析目标文件,目标文件可能不包括所有依赖项。所以目标文件的分析不会给我我正在寻找的信息。谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-30
    • 1970-01-01
    • 2012-01-06
    • 2011-02-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多