Declaring an abstract class (pure virtual method) significantly increases binary size

Here's the story: I am developing C ++ software for the ARM Cortex-M0 processor on Linux using the AC6 Toolpack. Before I used Keil (in windows) (who have their own toolchain) and I switched to the GNU-toolchain ((GNU Tools for ARM Embedded Processors) 5.2.1). The first thing I realized; the size of the binary file has increased significantly. I tested every compiler optimization (with the exception of link time optimization, it gives an error in the built-in assembly, not in part of the question, but may be related to the answer). Then he began to check executable files (elf file is not bin, gnu produces both) with any tool available: objdump, readelf, nm. I found several characters that cause an increase in size, the significant ones are: ' d_print_comp_inner ', ' d_exprlist ', ' d_template_args '. But I have no idea what causes these functions to appear in binary format. (I used minimal libraries: nano newlib). In short, I began to eliminate codes one by one to find the culprit. Finally, it was an abstract method declaration!

Function definition as

 virtual Return_type function_name(...)=0; 

instead

  virtual Return_type function_name(...); 

adding 45 kb and the characters i mentioned. And this is the only change in the source code. There is an empty definition in the base class. Note: the method is still virtual and is overridden in child classes

Output size without an abstract class:

  text data bss dec hex filename 15316 24 4764 20104 4e88 temc_discovery.elf 

Output size with abstract class:

  text data bss dec hex filename 61484 128 4796 66408 10368 temc_discovery.elf 

Here the characters and their size, which appears when the method is abstract, eliminate those that appear in both versions. ( nm tool is used. Not a complete list, those with size> = 0x60)

 00002de4 t d_print_comp_inner 00001a34 t d_exprlist 00000ca4 t d_template_args 00000678 t d_type 00000574 t d_print_mod 000003f8 t d_encoding 000003e0 r cplus_demangle_operators 000003c8 t d_expression_1 000003a8 t d_name 00000354 t d_demangle_callback.constprop.15 000002e0 t d_print_mod_list 00000294 r cplus_demangle_builtin_types 00000268 t d_unqualified_name 00000244 T _printf_i 00000238 t d_print_function_type.isra.11 000001fc T _svfprintf_r 000001fc T _svfiprintf_r 000001f4 t d_print_array_type.isra.10 000001ce t d_print_cast.isra.12 0000018c t d_substitution 00000110 t d_operator_name 0000010c T __sflush_r 000000e8 T __swsetup_r 000000e6 t d_cv_qualifiers 000000e0 t d_print_subexpr 000000e0 t d_expr_primary 000000dc T _printf_common 000000cc T __cxa_demangle 000000c8 t d_source_name 000000c4 r standard_subs 000000c4 T __ssputs_r 000000b0 T __swbuf_r 000000ac T _malloc_r 000000a8 T _fputs_r 000000a4 T __smakebuf_r 000000a0 T __gnu_cxx::__verbose_terminate_handler() 00000096 t d_print_expr_op 0000008c T _free_r 0000008c t d_parmlist 0000008a t d_growable_string_callback_adapter 0000007c T __sfp 00000072 t d_append_buffer 00000068 T __sinit 00000060 d impure_data 

Some names familiar to me (e.g. printf, flush, malloc, fputs, etc.) are not even mentioned in the source code.

Does anyone know what causes this behavior?

Update: I have already disabled exceptions with the --noexception flag, so I have not received any references to it. As it turned out, this is due to the fact that it’s so good to talk about it here.

Update 2: This is the most comprehensive website explaining all this if you are tracking links in the responses.

+7
c ++ arm cortex-m gnu-toolchain
source share
2 answers

This is almost certainly due to the unexpected inclusion of exception handling that libC ++ has built into it, regardless of whether you compile your code with --noexception or no matter what gnu-ism is.

The exception is probably a "pure virtual function call" or something like that (a rather implicit runtime error to get, but possible if you call virtual functions in the base class constructor).

The answer is to provide your own empty implementation of this, atexit () and any random callout that you really don't need. Once you do this, the linker will not drag other stuff (which drags other things, which drags other things, etc.).

 void __cxa_pure_virtual(void) { BKPT(); } 

This is what I have in our project, although something has probably changed in your version of libC ++

+6
source share

As far as I understand, when you do your virtual function in a pure base class, you create the potential to create a pure virtual call. Thus, the compiler generates code in which it prints a message about the true fact of the virtual call, the unmanned names for the function and class, and may be something else. It adds a bunch of functions to your binary, so the size increases.

I suggest adding an empty implementation to your pure virtual function - perhaps this prevents the compiler from doing this.

+5
source share

All Articles