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

Нечасто возникает необходимость включить в прошивку какие-либо файлы. Однако, если это необходимо, то начинается головная боль о том, как это сделать с наименьшими усилиями и максимально красиво.
Если у вас возникла такая задача, прошу прочитать далее, где мы рассмотрим несколько вариантов для решения подобной задачи. Какие-то варианты будут на костылях, а какие-то максимально простые.
Всё описанное ниже было довольно давно, просто сейчас наткнулся на статью в одном из блогов, где автор предлагает использовать онлайн-сервис или программу для решения данной задачи.
Поехали...
Предыстория
Появилась задача написать программу-обновлялку для одного из устройств, которая позволяла бы перенести со старой версии прошивки на новую.
Адресное пространство старой и новой, а так же размер разделов, структура раздела с конфигом и многое другое были не совместимы, а переезд планировался с сохранением всей необходимой информацией. Но это не самое сложное.
Необходимо было загрузить прощивку-мигратор, которая при старте обновляет не только статические данные, но и бутлоадер, а это уже какой-никакой, а файл, вот его и пришлось закидывать в прошивку программы-мигратора (на самом деле не только его, но это не важно).
Что мы имеем:
- Исходники программы-мигратора на языке Си;
- Бинарник программы-обновлялки и ещё нескольких других программ (хорошо было бы собирать перед сборкой прошивки), которые необходимо включить в прошивку;
- GCC со всеми вытекающими.
Что делать?
Попробуем рассмотреть варианты, которыми можно выполнить нашу задачу:
- Написать скрипт, который подготовит из бинарника Сишный массив (так себе вариант);
- Написать маленький ассемблерный код, где будет включен наш бинарник (очень простой);
- Использовать Binutils и описать лишь цели в makefile (красивый вариант).
- Самый красивый вариант.
Так себе вариант
Пишем скрипт, который будет при сборке, а может и просто при запуске отдельно, генерировать сишный файл с массивом и константой. Или, чего хуже, использовать какие-то программы с графическим интерфейсом или онлайн-сервисы.
Но в итоге будет что-то вроде этого:
uint8_t updater_data[] = {0x00, 0x10, 0x00, 0x20, 0x00, 0x00, 0x00, 0x08, 0x84 ... };
size_t updater_data_len = 0x200;
Чем это плохо?
- много ручной работы, скрипт писать, копировать;
- раздувание кодовой базы;
- ненужные файлы в системе контроля версий.
Очень простой
Необходимо написать небольшой ассемблерный файл, который будет компилится вместе с остальными файлами проекта.
Файл должен содержать примерно следующие инструкции.
AREA updater_Section, DATA, READONLY
EXPORT updater_data
updater_data
INCBIN updater.bin
updater_end
updater_length
DCD updater_end - updater_data
EXPORT updater_length
END
Это ARM ассемблер для кейловского компилятора. Но суть ясна, мы инклудим наш файл и создаем переменную, которая содержит размер файла.
Казалось бы нет ничего проще, но давайте рассмотрим следующий вариант.
Красивый вариант
Не могу утверждать, что это самый красивый и правильный вариант, но это тот, который мне нравиться больше, чем остальные.
Здесь мы не пишем никакой программный код, а лишь при сборке проекта вызываем нужные утилитки с нужными параметрами.
Нужно просто создать цель в makefile
$(OBJ_DIR)/bin_updater.o: $(OBJ_DIR)
@objcopy -I binary --output-target elf32-littlearm --rename-section .data=.bin_files ./updater.bin $@
Секцию переименовывать не обязательно, просто мне нравится, что данные будут лежать отдельно.
Далее необходимо в процессе линковки просто подсунуть наш объектный файл.
Теперь разберем как это использовать:
extern const uint8_t _binary___updater_bin_start[];
extern const uint8_t _binary___updater_bin_end[];
void func1(void)
{
/* Например перебор байтов */
for (uint8_t* p = _binary___updater_bin_start; p < _binary___updater_bin_end; p++)
{
/* CODE */
}
}
Самый красивый вариант
const uint8_t updater_data[] = {
#embed uint8_t "updater.bin"
};
Красиво, неправда ли? Но поспешу огорчить такой вариант это лишь предложение для включения в новый стандарт языка Си.
Вместо выводов
Выбирайте вариант, который вам больше всего нравится, а если есть что-то отличное от этого пишите в комментариях.