Процесс уменьшения размера исполняемого файла


Я создаю шестнадцатеричный файл для запуска на процессоре ARM, который я хочу сохранить ниже 32K. в настоящее время он намного больше, и мне интересно, может ли кто-то дать совет о том, как лучше всего уменьшить его?

Вот что я сделал до сих пор

  1. поэтому я запустил "размер", чтобы определить, насколько велик шестнадцатеричный файл.
  2. затем снова "размер", чтобы увидеть, насколько велик каждый из объектных файлов, которые связаны с созданием шестнадцатеричных файлов. Похоже, что большая часть размера происходит от внешняя библиотека.
  3. Затем я использовал "readelf", чтобы посмотреть, какие функции занимают больше всего памяти.
  4. я просмотрел код, чтобы увидеть, могу ли я исключить вызовы этих функций.

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

Итак, каковы следующие шаги?

Ответ на ответы:

  • Как я могу видеть там вызываются функции, которые занимают много памяти. Однако я не могу найти, как это называется.
  • я хочу опустить эти функции (если это возможно), но я не могу найти, что их вызывает! Может быть вызван из любого числа библиотечных функций, я думаю.
  • компоновщик работает по желанию, я думаю, он включает только соответствующие файлы библиотеки. Как узнать, включены ли только соответствующие функции? Вы можете установить флаг или что-то для этого?
  • я использую GCC
8 12

8 ответов:

Общий список:

  • убедитесь, что параметры отладки компилятора и компоновщика отключены
  • компиляция и связывание с включенными опциями размера (- Os в gcc)
  • выполнить strip на исполняемом файле
  • создайте файл карты и проверьте размеры функций. Вы можете либо заставить компоновщик сгенерировать файл карты (-M при использовании ld), либо использовать objdump для конечного исполняемого файла (обратите внимание, что это будет работать только с несвязанным исполняемым файлом!) Это на самом деле не исправит проблема, но это позволит вам узнать о самых страшных преступниках.
  • Используйте nm для исследования символов, вызываемых из каждого объектного файла. Это должно помочь в поиске вызывающих функций, которые вы не хотите вызывать.

В первоначальном вопросе был под-вопрос о включении только соответствующих функций. gcc будет включать все функции в каждом используемом объектном файле. Другими словами, если у вас есть объектный файл, содержащий 10 функций, все 10 функции включаются в исполняемый файл, даже если одна из них действительно вызывается.

Стандартные библиотеки (напр. libc) разбивает функции на множество отдельных объектных файлов, которые затем архивируются. Затем исполняемый файл связывается с архивом. При разбиении на множество объектных файлов компоновщик может включать только те функции, которые действительно вызываются. (это предполагает, что вы статически связываете)

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

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

Другой оптимизацией, которая может сэкономить вам работу, является -ffunction-sections,-Wl, - gc-sections, если вы используете GCC. Однако хорошей цепочке инструментов не нужно будет говорить об этом.

Пояснение: GNU ld связывает разделы, и GCC выдает по одному разделу на единицу перевода, если вы не скажете иначе. Но в C++ узлами графа dependecy являются объекты и функции.

Просто для того, чтобы перепроверить и документировать для дальнейшего использования, но используете ли вы инструкции большого пальца? Это 16-битные версии обычных инструкций. Иногда вам могут понадобиться 2 16-битные инструкции, поэтому это не сэкономит 50% пространства кода.

Приличный линкер должен брать только необходимые функции. Однако вам могут понадобиться настройки компилятора и linke, чтобы упаковать функции для индивидуальной компоновки.

В глубоко внедренных проектах я всегда стараюсь избегать использования каких-либо стандартных библиотечных функций. Даже простые функции, такие как "strtol ()", взрывают двоичный размер. Если возможно, просто избегайте этих звонков.

В большинстве глубоко внедренных проектов вам не нужно универсальное" printf () " или динамическое выделение памяти (многие контроллеры имеют 32 кб или меньше ОЗУ).

Вместо простого использования "printf ()" я использую очень простой пользовательский " printf ()", эта функция может печатать только числа в шестнадцатеричном или десятичный формат, не больше. Большинство структур данных предварительно распределяются во время компиляции.

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

Должно быть, это лучший способ сделать это.

У Эндрю Эджкомба отличный список, но если вы действительно хотите очистить каждый последний байт, sstrip - хороший инструмент, который отсутствует в списке и может сбрить еще несколько КБ.

Например, при запуске на strip себе, он может сбрить ~2kB .

Из старого README (смотрите комментарии вверху этого косвенного исходного файла):

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

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

Обратите внимание, что из-за некоторой информации, которую он удаляет, исполняемый файл sstrip'D, по слухам, имеет проблемы с некоторыми инструментами. Об этом подробнее говорится в комментариях источника.

Также... для развлекательного / сумасшедшего чтения о том, как сделать минимально возможный исполняемый файл, эта статья стоит прочитать.

Чтобы ответить на эту конкретную потребность:

•я хочу опустить эти функции (если это возможно), но я не могу найти то, что зову их!! Может быть вызван из любого числа библиотечных функций I догадываться.

Если вы хотите проанализировать свою кодовую базу, чтобы увидеть, кто что вызывает, кем вызывается данная функция и тому подобное, есть отличный инструмент под названием "понять C", предоставляемый SciTools.

Https://scitools.com/

Я использовал его очень часто в прошлом выполняли статический анализ кода. Это действительно может помочь определить дерево зависимостей библиотеки. Это позволяет легко просматривать вверх и вниз дерево вызовов среди прочего.

Они предоставляют ограниченное время оценки, затем вы должны приобрести лицензию.

Вы можете посмотреть на что-то вроде сжатия исполняемого файла.