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

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

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

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


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

Update:

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

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

Ответ 1

Для 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

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

Ответ 2

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

Ответ 3

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

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

-dead_strip

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

-dead_strip_dylibs

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

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

-why_live symbol_name

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

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

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

Ответ 4

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

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

Мне кажется, что ответ, предоставленный Немо, правильный. Если эти инструкции не работают, проблема может быть связана с версией 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

Ответ 8

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

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

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

Ответ 9

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

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

Ответ 10

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

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

Ответ 11

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

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