Процесс уменьшения размера исполняемого файла - PullRequest
12 голосов
/ 14 октября 2008

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

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

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

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

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

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

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

Ответы [ 8 ]

13 голосов
/ 14 октября 2008

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

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

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

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

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

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

5 голосов
/ 14 октября 2008

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

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

2 голосов
/ 09 декабря 2008

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

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

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

2 голосов
/ 14 октября 2008

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

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

1 голос
/ 26 апреля 2012

Andrew EdgeCombe имеет отличный список, но если вы действительно хотите очистить каждый последний байт, sstrip - это хороший инструмент, который отсутствует в списке и может сократить на несколько килобайт.

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

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

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

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

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

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

1 голос
/ 14 октября 2008

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

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

0 голосов
/ 24 июля 2015

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

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

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

https://scitools.com/

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

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

0 голосов
/ 14 октября 2008

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

...