Как я могу проанализировать размер образа моей прошивки?

В настоящее время мы разрабатываем прошивку для продукта 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

на libstdc++. Затем используйте -Wl,--gc-sections чтобы избавиться от этих разделов.

Вы также можете попробовать использовать -flto

он должен сделать то же самое для вас, но я столкнулся с проблемами при сборке newlib и libstdc++ с lto. Похоже, что у инструментария сборки libstdc++ есть проблемы с архивами newlib. Тем не менее, lto - это то, что вы должны предпочесть, когда это возможно, поскольку он также хорошо обнаруживает и нарушения ODR или другой неработающий код, который может скрыть поэтапная компиляция.
Другие вопросы по тегам