Как удалить неиспользуемые символы C / C++ с помощью GCC и ld?


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

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

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


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

обновление:

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

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

11 93

11 ответов:

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

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

-fdata-sections -ffunction-sections

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

-Wl,--gc-sections

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

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

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

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

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

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

-dead_strip

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

-dead_strip_dylibs

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

и этот полезный вариант

-why_live symbol_name

регистрирует цепочку ссылок на symbol_name. Только с -dead_strip. Это может помочь отладить, почему то, что вы думаете, должно быть мертвой полосы удалены не удаляется.

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

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

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

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

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

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

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

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

мне кажется, что ответ Немо является правильным. Если эти инструкции не работают, проблема может быть связана с версией 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 байт, соответственно, самый существенный вклад, поступающий от декларации "strip-all". Если вы не можете произвести подобные сокращения на вашей платформе,то, возможно, ваша версия gcc не поддерживает эту функцию. Я использую gcc(4.5.2-8ubuntu4), ld(2.21.0.20110327) на Linux Mint 2.6.38-8-generic x86_64

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

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

вы может также найти некоторые ответы на это аналогичный вопрос использования.

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

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

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

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

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

Примечание: он изменяет сам файл и не создает копию.