Как удалить неиспользуемые символы C / C ++ с помощью GCC и ld? - PullRequest
100 голосов
/ 14 июля 2011

Мне нужно серьезно оптимизировать размер моего исполняемого файла (ARM development) и Я заметил, что в моей текущей схеме сборки (gcc + ld) неиспользуемые символы не удаляются.

Использование arm-strip --strip-unneeded для полученных исполняемых файлов / библиотек не меняет выходного размера исполняемого файла (я понятия не имею, почему, может быть, просто не может) .

Каков будет (если он существует) , чтобы изменить мой строительный конвейер так, чтобы неиспользуемые символы были удалены из результирующего файла?


Я бы даже не подумал об этом, но моя текущая встроенная среда не очень «мощная» и сохранение даже 500K из 2M приводит к очень хорошему повышению производительности загрузки.

Обновление:

К сожалению, текущая версия gcc, которую я использую, не имеет опции -dead-strip, а -ffunction-sections... + --gc-sections для ld не дает какой-либо существенной разницы для результирующего вывода.

Я шокирован, что это даже стало проблемой, потому что я был уверен, что gcc + ld автоматически удалит неиспользуемые символы (почему они вообще должны их оставлять?).

Ответы [ 11 ]

120 голосов
/ 21 июля 2011

Для GCC это выполняется в два этапа:

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

-fdata-sections -ffunction-sections

Свяжите единицы перевода вместе, используя флаг оптимизации компоновщика (это заставляет компоновщик отбрасывать несвязанные разделы):

-Wl,--gc-sections

Итак, если у вас был один файл с именем test.cpp, в котором были объявлены две функции, но одна из них не использовалась, вы можете опустить неиспользуемый файл с помощью следующей команды для gcc (g ++):

gcc -Os -fdata-sections -ffunction-sections test.cpp -o test -Wl,--gc-sections

(Обратите внимание, что -Os - это дополнительный флаг компилятора, который указывает GCC оптимизировать размер)

33 голосов
/ 14 июля 2011

Если этому потоку нужно верить, вам нужно предоставить -ffunction-sections и -fdata-sections для gcc, который поместит каждую функцию и объект данных в отдельный раздел , Затем вы даете и --gc-sections GNU ld для удаления неиспользуемых разделов.

24 голосов
/ 14 июля 2011

Вы хотите проверить свои документы на наличие версии gcc & ld:

Однако для меня (OS X gcc 4.0.1) я нахожу их для ld

-dead_strip

Удаление функций и данных, которые недоступны для точки входа или экспортируемых символов.

-dead_strip_dylibs

Удалите дизели, которые недоступны точкой входа или экспортированными символами. То есть подавляет генерацию команд загрузки для dylibs, которые не указали символы во время ссылки. Эту опцию не следует использовать при связывании с dylib, который требуется во время выполнения по некоторой косвенной причине, например, у dylib есть важный инициализатор.

И эта полезная опция

-why_live symbol_name

Регистрирует цепочку ссылок на имя_символа. Применяется только с -dead_strip. Это может помочь отладить, почему то, что, по вашему мнению, должно быть удалено, не удалено.

В gcc / g ++ man также есть примечание, что определенные виды удаления мертвого кода выполняются только при включенной оптимизации при компиляции.

Хотя эти параметры / условия могут не подходить для вашего компилятора, я предлагаю вам найти что-то похожее в ваших документах.

19 голосов
/ 16 июля 2011

Навыки программирования тоже могут помочь; например добавить static к функциям, к которым нет доступа вне определенного файла; используйте более короткие имена для символов (может помочь немного, вероятно, не слишком); используйте const char x[], где это возможно; ... этот документ , хотя в нем говорится о динамических общих объектах, может содержать предложения, которые, если им следовать, могут помочь уменьшить конечный размер двоичного вывода (если ваша цель - ELF).

15 голосов
/ 18 июля 2014

Ответ -flto.Вы должны передать его на этапы компиляции и компоновки, в противном случае он ничего не делает.

На самом деле он работает очень хорошо - уменьшил размер написанной мной программы для микроконтроллера до менее чем 50% от ее предыдущегоsize!

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

13 голосов
/ 14 июля 2011

Хотя не строго по символам, если идет по размеру - всегда компилируйте с флагами -Os и -s. -Os оптимизирует полученный код для минимального размера исполняемого файла, а -s удаляет таблицу символов и информацию о перемещении из исполняемого файла.

Иногда - если требуется небольшой размер - игра с разными флагами оптимизации может иметь, а может и нет, иметь значение. Например, переключение -ffast-math и / или -fomit-frame-pointer может иногда экономить даже десятки байтов.

11 голосов
/ 18 июля 2011

Мне кажется, что ответ, предоставленный Немо, является правильным.Если эти инструкции не работают, проблема может быть связана с версией gcc / ld, которую вы используете, в качестве упражнения я скомпилировал пример программы, используя подробные инструкции здесь

#include <stdio.h>
void deadcode() { printf("This is d dead codez\n"); }
int main(void) { printf("This is main\n"); return 0 ; }

Затем я скомпилировал код, используя прогрессивно более агрессивные ключи удаления мертвого кода:

gcc -Os test.c -o test.elf
gcc -Os -fdata-sections -ffunction-sections test.c -o test.elf -Wl,--gc-sections
gcc -Os -fdata-sections -ffunction-sections test.c -o test.elf -Wl,--gc-sections -Wl,--strip-all

Эти параметры компиляции и компоновки дали исполняемые файлы размером 8457, 8164 и 6160 байт соответственно, наиболее существенный вклад внеслидекларация "раздеть все".Если вы не можете произвести аналогичные сокращения на своей платформе, то, возможно, ваша версия gcc не поддерживает эту функцию.Я использую gcc (4.5.2-8ubuntu4), ld (2.21.0.20110327) в Linux Mint 2.6.38-8-generic x86_64

8 голосов
/ 14 июля 2011

strip --strip-unneeded работает только с таблицей символов вашего исполняемого файла. На самом деле он не удаляет исполняемый код.

Стандартные библиотеки достигают желаемого результата, разбивая все свои функции на отдельные объектные файлы, которые объединяются с помощью ar. Если вы затем связываете результирующий архив как библиотеку (т.е. задаете опцию -l your_library для ld), тогда ld будет включать только объектные файлы и, следовательно, символы, которые фактически используются.

Вы также можете найти ответы на этот похожий вопрос об использовании.

4 голосов
/ 16 июня 2013

Из руководства GCC 4.2.1, раздел -fwhole-program:

Предположим, что текущий модуль компиляции представляет всю компилируемую программу.Все общедоступные функции и переменные, за исключением main и те, которые объединены с помощью атрибута externally_visible, становятся статическими функциями и в результате становятся более агрессивно оптимизированными межпроцедурными оптимизаторами.Хотя этот параметр эквивалентен правильному использованию ключевого слова static для программ, состоящих из одного файла, в сочетании с параметром --combine этот флаг можно использовать для компиляции большинства программ на C меньшего масштаба, поскольку функции и переменные становятся локальными для всегообъединенный модуль компиляции, а не для одного исходного файла.

4 голосов
/ 17 июля 2011

Я не знаю, поможет ли это вашему текущему затруднительному положению, поскольку это недавняя функция, но вы можете указать видимость символов в глобальном масштабе. Передача -fvisibility=hidden -fvisibility-inlines-hidden при компиляции может помочь компоновщику позже избавиться от ненужных символов. Если вы создаете исполняемый файл (в отличие от разделяемой библиотеки), вам больше нечего делать.

Более подробная информация (и детальный подход, например, к библиотекам) доступна на вики GCC .

...