Использование функций указателя - 2 отдельных приложения на 1 устройстве
Я задал некоторое время назад этот вопрос Как я могу использовать одну функцию из основного приложения и загрузчика? (встроенный) и начал реализовывать предлагаемое решение, но столкнулся с несколькими проблемами.
На моем Cortex M4 у меня есть 2 отдельных приложения - загрузчик и пользовательское приложение. Теперь у меня было несколько (много) функций, которые были одинаковыми для обоих приложений. Поэтому я скомпилировал их только для загрузчика, а затем создал массив указателей на функции по указанному адресу, который известен пользовательскому приложению. Поэтому в приложении я не собирал файлы с этими функциями снова, но использую эти указатели всякий раз, когда это необходимо.
Это пример кода, который я попытался сделать общим для обоих приложений:
static uint8_t m_var_1;
// Sends events to the application.
static void send_event(fs_op_t const * const p_op, fs_ret_t result)
{
uint8_t var_2;
[...]
}
Мое приложение заканчивается в Hardfault, что происходит, например, при делении на ноль или использовании указателя на функцию со значением NULL. Я не уверен почему, но я начал задаваться вопросом, что происходит с этими переменными. var_2
наверняка будет расположен в стеке, так что это не проблема. Но что насчет m_var_1
? В файле карты он имеет указанное место в оперативной памяти. Но у меня нет отдельных разделов RAM для приложения и загрузчика. Я не уверен, но у меня есть ощущение, что эта переменная может использовать то же место в оперативной памяти, что и при создании загрузчика. Это возможно? Может быть, другие проблемы?
1 ответ
Да, вы правы, код попытается получить доступ к глобальной переменной в том же месте, где она связана для загрузчика. Это связано с тем, что связывание подразумевает замену всех вхождений идентификаторов (включая имена функций и имена переменных) на адреса, определенные после компиляции.
В вашем приложении переменная, даже если она там существует, скорее всего, будет находиться по другому адресу. Вызов функций происходит, потому что они находятся в ПЗУ и не могут быть разными для приложения и загрузчика. Вызов их через константные указатели, которые также хранятся в ПЗУ, обходит проблему.
Решение заключается в использовании симулятора файловой системы, если вы можете найти его для своего оборудования.
В противном случае вы будете ненавидеть делать следующее.
Часть 1, настройка:
- ввести специальный раздел компоновщика со всеми переменными, доступными как системным папкам (приложение и загрузчик)
- пусть один линкер заполнит его
- установите его для другого компоновщика как не-tocuh
- будьте осторожны с инициализацией
- желательно не принимать значение инициализации
- если вам нужна инициализация, например, "bss" (init до 0) или "data" (init до указанного значения),
сделайте это явно в начале системной части, которая не связана с компоновщиком, который вы разрешаете устанавливать переменные - в целях безопасности рекомендуется выполнять инициализацию одинаково в обеих частях системы.
- "data" init использует специальный раздел энергонезависимого компоновщика с копией инициализируемых переменных, доступ к которым возможен
Часть 2, доступ:
- Опция 1)
хранить константные указатели на эти переменные, как вы делали для функций - вариант 2)
получить второй компоновщик (другой, который не выполнил фактическую настройку секции общих переменных), чтобы создать идентично структурированный и идентично расположенный раздел как секцию из первого компоновщика; необходимо больше изучения вашего линкера
Часть 3, восстановление значений, хранящихся в другой системной части
(например, вы хотите оставить какое-то сообщение от загрузчика, чтобы прочитать мое приложение)
- спроектировать, какая системная часть инициализирует какую переменную, другая только читает их
- разделите общие переменные на четыре части,
- записывается и читается обеими частями системы, инициализируется обеими
- написанный и прочитанный x, только прочитанный y, инициализированный x
- написанный и прочитанный y, только прочитанный x, инициализированный y
- записано обеими частями системы, не инициализировано, использует контрольные суммы и проверки достоверности,
если переменная не была инициализирована, init по умолчанию
- инициировать каждый раздел только в соответствующей части системы записи
- настроить как "без инициализации" в другом компоновщике
- установить как "без инициализации" в обоих компоновщиках для четвертого случая
- использовать геттеры и сеттеры с обновлением контрольной суммы и правдоподобием для четвертого случая
Чтобы сделать все это, требуется тщательное изучение возможностей и синтаксиса компоновщика.
Поэтому я рекомендую не пытаться, если вы можете обойти это. Рассмотрите возможность использования существующего симулятора файловой системы; потому что это в основном то, что выше означает.