Как построить и связать совместное использование проекта "системные вызовы" с загрузчиком в виде одноэлементного объекта?
Я пытаюсь создать решение, в котором будет два проекта: "загрузчик" (запуск после сброса и выполнение чего-л.) И "основное приложение", получающее управление из загрузчика.
Первоначально я только что воспроизвел пример отсюда: https://visualgdb.com/tutorials/arm/bootloader/
В последней части этого руководства описывается "системный вызов" - передача указателя на некоторую функцию, находящуюся в начальном загрузчике, в основное приложение, а затем выполнение вызова этой функции оттуда.
Намерение состоит в том, чтобы передать не указатель на функцию, а сказать указатель на объект класса.
Модифицированный пример из учебника выглядит так:
Загрузчик:
//sys.h
class SysCalls
{
public:
SysCalls();
int sum(int, int);
};
//sys.cpp
#include "sys.h"
SysCalls::SysCalls()
{
}
int SysCalls::sum(int a, int b)
{
return a + b;
}
// main.cpp
#include <sys.h>
...
SysCalls _sys;
void *g_Syscalls[] __attribute__((section(".syscalls"))) = { (void *)&_sys };
Основное применение:
//main.cpp
#include <sys.h> // the same header as in bootloader
extern "C" void *g_Syscalls[];
SysCalls *_sys = (SysCalls*) g_Syscalls[0];
int main(void)
{
...
int sum = _sys->sum(1, 2);
...
Я получаю ошибку компоновщика:
undefined reference to `SysCalls::sum(int, int)'
что предсказуемо, но...
Интересно, есть ли хороший способ построить это? какой-нибудь конфиг линкера? или я должен также включить sys.cpp в mainApplication и сделать так, чтобы содержимое не было каким-либо образом включено в окончательный двоичный файл?
Кроме того, с нетерпением жду - если говорить о простом персонале, таком как показанная функция суммирования, который использует только стек, то это всего лишь проблема компоновщика, но если я хочу создать некий "системный сервис", скажем, объект singleton с некоторым использованием кучи, тогда вопрос был бы - есть ли какой-нибудь хороший способ заморозить часть кучи, используемой этим объектом, при передаче управления из загрузчика, где он был создан, в основное приложение, которое должно его использовать...
2 ответа
Указатель на объект с элементами данных, созданными загрузчиком, будет недействительным в приложении, если только ОЗУ загрузчика не будет назначено постоянно, а не будет повторно использовано приложением. Что само по себе потребует разделения памяти компоновщиком. Единственный тип класса, который был бы действителен без такого разделения памяти, - это класс, содержащий статические функции-члены, которые не используют статические данные, и поэтому единственным преимуществом указателя на класс будет наличие единственного указателя на коллекцию, если функции (что не может быть без заслуг).
В любом случае, хотя для управления вашими требованиями можно использовать какую-то конфигурацию компоновщика, сценарии компоновщика часто бывают загадочными и не переносимы между цепями инструментов. Более простым решением было бы создать векторную таблицу, заполненную указателями на ваши функции, классы или объекты (несмотря на предыдущие предостережения), и использовать директивы компоновщика и / или компоновщика компилятора, чтобы найти таблицу в ПЗУ в известном и зарезервированном месте, То же самое местоположение таблицы может быть затем помещено в карту ссылок вашего приложения. Ваши точки входа затем просто обеспечиваются путем доступа к этой таблице в виде массива указателей с известным индексом для каждой точки доступа.
Векторы не обязательно должны быть указателями на функции, их можно интерпретировать (путем приведения) как указатель на любую сущность. Снова драгоценные предостережения о валидности объекта в контексте приложения.
Этот подход используется многими компонентами программного обеспечения промежуточного программного обеспечения. Одним из примеров является промежуточное программное обеспечение SoftDevices BT NRF. В сценариях компоновщика он дает некоторое ОЗУ для статических объектов и некоторое пространство для локального стека. "Основная" программа исключает эту оперативную память из памяти (область флэш-памяти, занятая SDev, также исключается). Это работает очень хорошо.
Это не настоящий загрузчик - загрузчик - это отдельное существо, имеющее доступ к SDev), поскольку загрузчик NRF поддерживает OTA