2016-06-21 7 views
7

Вот история: Я разрабатываю программное обеспечение C++ для процессора ARM Cortex-M0 в Linux с помощью AC6 Toolpack. До того, как я использовал Keil (в окнах) (у кого есть своя инструментальная цепочка), и я перешел на GNU-toolchain ((GNU Tools for ARM Embedded Processors) 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 KB и символы, которые я упомянул. И это единственное изменение в исходном коде. Существует пустое определение в базовом классе. Обратите внимание, что: метод еще виртуальный и переопределены в дочерних классах

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

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, утопленного, таНос, fputs и т.д.) даже не упоминается в источнике код.

Любое лицо с какой-либо мыслью, что вызывает подобное поведение?

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

Update 2: This is the most comprehensive website объяснить все это, если вы отслеживать ссылки в ответах.

+0

Это может быть лучше, чтобы обеспечить команду компиляции и ссылку, например, Опция '-g' будет генерировать большие двоичные файлы для отладочных символов и т. д. И вы можете проверить размер разделенного двоичного файла. – Mine

+0

Как мне грустно, я пробовал все оптимизации компилятора. Тот же результат (с более высоким увеличением 40 КБ). – ifyalciner

+2

Решение. вероятно, уже здесь: https://stackoverflow.com/questions/14689639/can-i-disable-exceptions-for-when-a-pure-virtual-function-is-called – deniss

ответ

6

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

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

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

void __cxa_pure_virtual(void) 
{ 
    BKPT(); 
} 

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

+0

Спасибо за ответ. Это сработало. – ifyalciner

5

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

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

+0

Только изменение кода - '= 0'. Поэтому, чтобы не получить функцию, не определенную ошибку, у меня уже есть пустое определение функции. – ifyalciner

+0

См. Http://stackoverflow.com/questions/99552/where-do-pure-virtual-function-call-crashes-come- from –

+0

@ifyalciner, вы можете иметь как = 0 для функции, так и пустую реализацию, например. виртуальный деструктор. – user2807083