Как я могу проанализировать размер образа моей прошивки?
В настоящее время мы разрабатываем прошивку для продукта 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-стек, тонкий веб-сервер, ...), Я не могу избавиться от них, поэтому я хотел бы максимально уменьшить размер и сделать его возможным, удалив функции, которые мне не нужны.
2 ответа
Учитывая размер отдельных функций не является разумным подходом. Одна "крошечная" функция может иметь сотни одинаково крошечных зависимостей в графе вызовов, которые в совокупности составляют огромный кусок. Например, для следующего:
int main()
{
for(;;)
{
do_statemachine() ;
}
return 0 ;
}
main()
будет крошечным, но в конечном итоге приведет к тому, что все остальные приложения будут связаны do_statemachine()
делает, который может быть любого размера. Вам необходимо учитывать общий размер функции и все ее зависимости.
Также необходимо учитывать общий размер инициализаторов статических или постоянных данных, также хранящихся в ПЗУ.
Вы должны использовать компоновщик для создания файла карты и графа вызовов - это, скорее всего, более полезно, чем использование nm после события.
Что касается конкретных символов в вашем вопросе, вы должны спросить себя, что вы называете в stdio? Например printf
нужен доступ к потоку (для stdout
), разбор спецификаторов формата и обход переменных аргументов - все это обеспечивается vfprintf
, Если бы это было не так, у вас был бы дублированный код, и хотя вы могли бы связать меньшее количество функций, все они были бы очень большими и потенциально демонстрировали бы другое поведение. Тот факт, что у вас есть "файловые" ориентированные функции в ссылке, не является проблемой; stdio работает с потоком, а не с файлами - "файл" является концептуальным, а не физическим. Если вы не подключили свою библиотеку к файловой системе (или если она еще не предоставлена в инструментах), код файловой системы не будет включен. Низкоуровневый доступ к потоку выполняется низкоуровневыми функциями ввода-вывода, которые могут поддерживать или не поддерживать доступ к файлам.
Другая возможность состоит в том, что в библиотеке отсутствует гранулярность - если все эти функции были определены в одном и том же объектном модуле, у компоновщика не будет иного выбора, кроме как связать их все, даже если на них нет ссылок. That might explain why you have integer, floating point and wide-character versions in the link.
Там, где появились эти символы, есть больше символов, связанных с wchar, которые вам не нужны. Вы можете избавиться от этих символов, создав инструментальную цепочку ESP32 с
-fdata-sections -ffunction-sections
включен для newlib. Также установите флаг
--disable-wchar_t
-Wl,--gc-sections
чтобы избавиться от этих разделов.
Вы также можете попробовать использовать
-flto