Объявление абстрактного класса (чисто виртуальный метод) существенно увеличивает размер двоичного файла

Вот история:
Я занимаюсь разработкой программного обеспечения C ++ для процессора ARM Cortex-M0 в Linux с помощью AC6 Toolpack. До того, как я использовал Keil (в Windows) (у которого есть свой собственный набор инструментов), я перешел на GNU-toolchain ((Инструменты GNU для встроенных процессоров ARM) 5.2.1). Первое, что я понял, это; размер двоичного файла существенно увеличился. Я проверил каждую оптимизацию компилятора (кроме оптимизации времени компоновки, она выдает ошибку во встроенной сборке, не является частью вопроса, но может быть связана с ответом). Затем начал проверять исполняемые файлы (elf-файл, а не bin, gnu создает оба) любым доступным инструментом: objdump, readelf, nm. Я нашел некоторые символы, вызывающие увеличение размера, значимые из них:d_print_comp_inner‘,’d_exprlist‘,’d_template_args». Но понятия не имею, что вызывает появление этих функций в двоичном виде. (я использовал минимальные библиотеки: nano newlib). Короче говоря, я начал устранять коды один за другим, чтобы найти виновника. Наконец-то это была декларация абстрактного метода!

Определение функции как

virtual Return_type function_name(...)=0;

вместо

 virtual Return_type function_name(...);

добавив 45 КБ и символы, которые я упомянул. И это единственное изменение в исходном коде. Пустое определение в базовом классе присутствует. Обратите внимание: метод все еще является виртуальным и переопределяется в дочерних классах.

Размер вывода без абстрактного класса:

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

Размер вывода с абстрактным классом:

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

Здесь символы и их размеры, которые появляются, когда метод абстрактный, исключены те, которые появляются в обеих версиях. (nm инструмент используется. Не полный список, с размером> = 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

Некоторые имена, знакомые мне (например, printf, flush, malloc, fputs и т. Д.), Даже не упоминаются в исходном коде.

Кто-нибудь с какой-либо идеей, что вызывает такое поведение?

Обновить:
Я уже отключал исключения с флагом --noexceptionтак что я не дал ни одного, хотя на это. Как выясняется, так приятно ответить здесь, упомянуть об этом здесь.

Обновление 2:
Это самый полный сайт все объясняю, если вы отслеживаете ссылки в ответах.

7

Решение

Это почти наверняка из-за неожиданного включения обработки исключений, встроенной в нее libc ++, независимо от того, компилируете ли вы свой код или нет --noexception или каким бы ни был правильный гнуизм.

Исключением является, вероятно, «чистый вызов виртуальной функции» или что-то в этом роде (довольно непонятная ошибка времени выполнения, которую можно получить, но это возможно, если вы вызываете виртуальные функции в конструкторе базового класса).

Ответ заключается в том, чтобы предоставить собственную пустую реализацию этого, atexit () и любой случайный вызов, который вам не нужен. Как только вы это сделаете, компоновщик не будет перетаскивать другие вещи (которые перетаскивают другие вещи, которые перетаскивают другие вещи и т. Д.).

void __cxa_pure_virtual(void)
{
BKPT();
}

Это то, что у меня есть в нашем проекте, хотя в вашей версии libc ++ все могло измениться

6

Другие решения

Насколько я понимаю, когда вы делаете свою виртуальную функцию в базовом классе чистый Вы создаете потенциал для совершения чисто виртуального звонка. Таким образом, компилятор генерирует код, в котором он печатает сообщение о чистом факте виртуального вызова, разделенных именах для функций и классов и может быть чем-то другим. Для этого он добавляет кучу функций в ваш двоичный файл, поэтому размер увеличивается.

Я предлагаю добавить пустую реализацию к вашей чисто виртуальной функции — может быть, это мешает компилятору делать такие вещи.

5

По вопросам рекламы [email protected]