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

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

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

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


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

Обновить:

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

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

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

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

     -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 оптимизировать размер)

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

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

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

     -dead_strip 

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

     -dead_strip_dylibs 

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

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

     -why_live symbol_name 

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

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

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

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

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

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

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

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

     #include  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

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

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

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

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

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

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

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

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

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

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

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

    Давайте будем гением компьютера.