关于c ++:声明抽象类(纯虚方法)会大大增加二进制大小

Declaring abstract class (pure virtual method) increase binary size substantially

这是故事:
我正在使用AC6 Toolpack在Linux中为ARM Cortex-M0处理器开发C ++软件。在使用Keil(在Windows中)(拥有自己的工具链)之前,我已经迁移到GNU工具链((用于ARM嵌入式处理器的GNU工具)5.2.1)。我意识到的第一件事是;二进制文件的大小大大增加。我已经测试了所有编译器优化(链接时间优化除外,它在内联汇编中给出错误,不是问题的一部分,但可能与答案有关)。然后开始使用任何可用的工具:objdump,readelf,nm检查可执行文件(elf文件不是bin,gnu都会生成)。我发现一些引起大小增加的符号,重要的是:'d_print_comp_inner','d_exprlist','d_template_args'。但是不知道是什么导致这些函数以二进制形式出现。 (我使用了最少的库:nano newlib)。长话短说,我开始一一消除代码以找到罪魁祸首。最后是抽象方法声明!

定义功能为

1
virtual Return_type function_name(...)=0;

代替

1
 virtual Return_type function_name(...);

添加45 KB和我提到的符号。这是源代码中的唯一更改。基类中存在空定义。注意:方法仍然是虚拟的,并且在子类中被覆盖

没有抽象类的大小输出:

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

使用Abstract类输出大小:

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

在这里,当方法抽象时显示的符号及其大小,消除了在两个版本中都显示的符号及其大小。 (使用nm工具。列表不完整,尺寸> = 0x60的列表)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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

在源代码中甚至没有提到我熟悉的一些名称(例如printf,flush,malloc,fputs等)。

任何人都知道是什么导致了这种现象?

更新:
我已经用标志--noexception禁用了异常,所以我什么也没给。事实证明,在这里提到这一点与回答有关。

更新2:
如果您跟踪答案中的链接,这是最全面的网站,解释了所有内容。


几乎可以肯定,这是因为libc ++已内置了异常处理功能,而不管它是否包含了异常处理功能,无论您是否使用--noexception编译代码或使用何种适当的gnu-ism。

有问题的异常可能是"纯虚函数调用"或类似的东西(一个相当模糊的运行时错误,但如果您在基类构造函数中调用虚函数,则可能出现)。

答案是提供您自己的空实现atexit()以及您真正不需要的任何随机标注。完成此操作后,链接器将不会拖入其他内容(拖入其他内容,拖入其他内容,等等)。

1
2
3
4
void __cxa_pure_virtual(void)
{
    BKPT();
}

我的项目是我的,尽管您的libc ++版本可能已更改


据我了解,当您在基类中将虚拟函数设为纯函数时,就会产生进行纯虚拟调用的可能性。因此,编译器生成代码,在该代码中打印有关纯虚拟调用事实的消息,函数和类的已拆名称,并且可能还有其他内容。为此,它向二进制文件添加了许多函数,因此大小增加了。

我建议为您的纯虚函数添加空的实现-可能是因为它阻止了编译器执行该操作。