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

StackOverflow https://stackoverflow.com/questions/200292

  •  03-07-2019
  •  | 
  •  

Вопрос

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

Вот что я сделал на данный момент

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

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

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

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

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

Решение

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

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

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

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

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

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

Другие советы

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

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

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

Хороший компоновщик должен выполнять только необходимые функции.Однако вам могут потребоваться настройки компилятора и компоновки для упаковки функций для индивидуального связывания.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

https://scitools.com/

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top