В настоящее время мы разрабатываем прошивку для продукта IoT на основе популярного чипа ESP32 с использованием их инфраструктуры ESP-IDF, которая создает двоичные файлы с использованием GCC / G ++ — Toolchain с именем xtensa
(https://github.com/icamgo/xtensa-toolchain).
Недавно я заметил, что размер двоичного файла стал довольно большим (всего лишь 1 МБ), и решил посмотреть и попытаться уменьшить его. NDEBUG
определено, -Os
включен и вывод strip
PED.
В основном, цепочка инструментов производит .elf
файл, поэтому я взглянул на его содержимое:
nm -S -C --size-sort <my-app>.elf
Шесть крупнейших функций (размером от 6 до 12 кБ):
4011b24c 0000187b T __ssvfscanf_r
400f9f38 00001ffa T _svfiprintf_r
400f2aa4 000020fe T _vfiprintf_r
4012005c 000030d2 T _svfwprintf_r
400ef4d8 000030de T _svfprintf_r
400f50dc 000031e6 T _vfprintf_r
Итак, самые большие функции в моем образе прошивки — это vfprintf и friends, добавляющие до 60 кБ только двоичного размера. Почему они такие большие? Как я могу уменьшить их размер или избавиться от них (мне вообще не нужен vfprintf, так как у меня нет файловой системы на микроконтроллере)?
Существуют ли другие методы для уменьшения размера двоичного файла? Как мне поступить в моем квесте?
Изменить (Уточнение причины оптимизации):
Существуют разные версии ESP32 с вплоть до 16 МБ флеш-памяти. Тот, который мы используем, имеет 4 МБ. 1 МБ зарезервировано для хранения закрепленных сертификатов сервера, параметров конфигурации доверенных URL-адресов. И, поскольку нам нужна функциональность OTA-обновления, нам необходимо оставить тот же объем флэш-памяти, который используется образом приложения, свободным для новой его версии. Это оставляет нам 1,5 МБ флэш-памяти для образа приложения, что не слишком далеко от нашего текущего 1 МБ. Поэтому я думаю, что стоит подумать об уменьшении размера, прежде чем эта проблема не позволит нам вообще вводить новые функции.
Я понимаю, что 60 КБ нежелательных функций vfprintf () — это небольшая часть 1 МБ, но нам действительно нужно много действительно полезных библиотек (mbedtls для шифрования, полный IP-стек, тонкий веб-сервер, …) , Я не могу избавиться от них, поэтому я хотел бы уменьшить размер насколько это возможно и возможно удаляя функции, я не пользуюсь ими.
Учитывая размер отдельных функций не является разумным подходом. Одна «крошечная» функция может иметь сотни одинаково крошечных зависимостей в графе вызовов, которые в совокупности составляют огромный кусок. Например, для следующего:
int main()
{
for(;;)
{
do_statemachine() ;
}
return 0 ;
}
main()
будет крошечным, но в конечном итоге вызывает все остальная часть приложения будет связана из-за чего-либо do_statemachine()
делает, что может быть любой размер. Вы должны учитывать общий размер функции и все его зависимости.
Также необходимо учитывать общий размер инициализаторов статических или постоянных данных, также хранящихся в ПЗУ.
Вы должны использовать компоновщик для создания файла карты и графа вызовов — это, скорее всего, более полезно, чем использование nm после события.
Что касается конкретных символов в вашем вопросе, вы должны спросить себя, что вы называете в stdio? Например printf
нужен доступ к потоку (для stdout
), разбор спецификаторов формата и обход переменных аргументов — то есть все предоставлено vfprintf
, Если бы это было не так, вы бы дублировали код, и хотя вы могли бы связать меньшее количество функций, все они были бы очень большой и потенциально проявляют другое поведение. Тот факт, что у вас есть «файловые» ориентированные функции в ссылке, не является проблемой; stdio работает с потоком, а не с файлами — «файл» является концептуальным, а не физическим. Если вы не подключили свою библиотеку к файловой системе (или если она еще не предоставлена в инструментах), код файловой системы не будет включен. Низкоуровневый доступ к потоку выполняется низкоуровневыми функциями ввода-вывода, которые могут поддерживать или не поддерживать доступ к файлам.
Другая возможность состоит в том, что в библиотеке отсутствует гранулярность — если все эти функции были определены в одном и том же объектном модуле, у компоновщика не будет иного выбора, кроме как связать их все, даже если на них нет ссылок. Это может объяснить, почему в ссылке есть целочисленные версии, версии с плавающей точкой и широкие символы.
Других решений пока нет …