2

Is there a tool that analyzes the size of compiled functions and their dependencies in a binary executable?

In my case, the program is written in C++, but most of the dependencies I am interested in analyzing come from C libraries. The platform is Linux.

My goal is to reduce the size of a statically linked version of the program. I am considering removing calls to library functions (and implementing my own replacements), but before I start doing that work, I would like to know which library functions contribute the most "bloat".

For example, consider the following program test.c:

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

This is, of course, a trivial example without any libraries, but I use it for illustrative purposes.

I compile this with gcc test.c.

The output of objdump -d a.out includes the following:

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

We can see at address 1154 that main calls foo.

So I'm looking for a tool that will tell me the following:

  • The size of main is 36 bytes.
  • The size of foo is 19 bytes.
  • The size of main and its dependencies is 55 bytes. (This is the tricky number to calculate, because the tool needs to perform a recursive descent search to find all the functions upon which main depends.)
  • main depends on the following functions: foo

It would be an added bonus if the tool also told me about the size of any static data structures that the functions use.

I've found the following related Stack Overflow questions, but it appears none of the answers mention a tool that does the dependency analysis I want.

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 looks nice, but appears to lack the dependency analysis I want.

mpb
  • 1,277
  • 15
  • 18

2 Answers2

0

Since it seems that you are using the GNU toolchain, a semi-easy way to do this is to use the option -ffunction-sections when you compile and the option -M" when you link the program.

The compiler option -ffunction-sections tells the compiler to split each function into its own .text section and the linker option -M tells the compiler to output the memory map to the console. The memory map table will include the size of each object.

For example, I compiled a program with two functions, main() and test() located in a single source file main.c. The output contains:

linker memory map example

Here the size of each function in bytes is underlined in red.

Note that this data will be buried amongst all of the other symbols defined in the program, including standard and other libraries linked with the program, so you will need to search for the function name. In the figure, I only show the contents of the table for the two functions that I defined.

Also note that this may not work for static functions, particularly if optimizations are enabled.

To identify the dependencies, you could use a program like cflow (https://www.gnu.org/software/cflow/manual/cflow.html) to generate a call graph and identify the dependencies through the call graph.

Alternatively, you could do it manually by creating a main() that calls the function that you would like to analyze, compiling with option -ffunction-sections then linking with options --gc-sections and -M. The map file will allocate functions that are used and list functions that are unused.

In either approach, you will probably need to find your own way to use the data to identify the size with dependencies.

Jonathon S.
  • 1,928
  • 1
  • 12
  • 18
  • But I also want the analysis tool to trace the function tree and tell me the total size of a function plus all the (sub-)functions it calls. (Note in my question: "The size of `main` and its dependencies is 55 bytes.") Your approach appears to only tell me the size of each function by itself. But I can (fairly) easily derive that from the `objdump` output in my question. Or am I missing something? Thanks! – mpb Oct 07 '21 at 06:27
0

Is there a tool that analyzes the size of compiled functions and their dependencies in a binary executable?

Yes, the linker, ld(1) does.

You can use the set of tools that come with the linker (mostly nm(1) or objdump(1)) to see the contents of an object file in textual form.

While you can do it in an executable file, it is normally better to do it with the object files .o.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31
  • So what command line arguments do I use to make `ld` print out (for example) the size of `printf` plus the size of all of `printf`'s dependencies? I want a tool that sums up **all** the sizes and gives me a **single number**. Regarding analyzing object files, an object file may not include all the dependencies. So analysis of object files will not give me the information I am looking for. Thanks! – mpb Oct 09 '21 at 17:36