Я использую arm-none-eabi-g ++ для компиляции для микроконтроллера ARM Cortex M. Мой код статически создает некоторые модули, инициализирует их и выполняет их последовательно в цикле. Каждый модуль (ModuleFoo
, ModuleBar
...) - это класс, который принадлежит к классу Module
. Класс Module
определяет две виртуальные функции: init()
и exec()
, которые реализованы в каждом производном модуле. Нет явных конструкторов, ни в базе, ни в производных классах. Наконец, у меня есть структура Context, которая передается и содержит список указателей на модули (Module* modules[]
).GCC ARM: vtable не инициализирован
я имел следующий код, который работал:
int main() {
ModuleFoo foo;
ModuleBar bar;
Context context;
const int N_MODULES = 2;
context.modules[0] = &foo; // Indexes are actually an enum but I stripped it to make it shorter
context.modules[1] = &bar;
for (int i = 0; i < N_MODULES; i++) {
context.modules[i]->init(context);
}
while (1) {
for (int i = 0; i < N_MODULES; i++) {
context.modules[i]->exec(context);
}
}
}
До сих пор, так хорошо (по крайней мере, я так думаю, в любом случае он работал).
Теперь я хочу, чтобы сделать систему более ремонтопригодны, перемещая весь код, связанный с «какие модули используются в конкретной конфигурации» в отдельный config.cpp
/config.h
файла:
config.cpp
:
ModuleFoo foo;
ModuleBar bar;
void initContext(Context& context) {
context.nModules = 2;
context.modules[0] = &foo;
context.modules[1] = &bar;
}
main.cpp
:
#include "config.h"
int main() {
Context context;
initContext(context);
for (int i = 0; i < context.nModules; i++) {
context.modules[i]->init(context);
}
while (1) {
for (int i = 0; i < context.nModules; i++) {
context.modules[i]->exec(context);
}
}
}
проблема возникает, когда init()
вызывается на первом модуле (MCU HardFaults). Это потому, что, в соответствии с GDB, то указатель виртуальных таблиц не инициализирован:
(gdb) p foo
$1 = {
<Module> = {
_vptr.Module = 0x0 <__isr_vector>,
_enabled = false
},
я откат с Git, чтобы проверить, с предыдущей структурой коды по виртуальным таблицам указателя был правильно инициализирован. И в соответствии с файлом карты компоновщиком и GDB, то существует виртуальные таблицы (примерно по тому же адресу, как и раньше):
.rodata 0x0000000000008e14 0x2c ModuleFoo.o
0x0000000000008e14 typeinfo name for ModuleFoo
0x0000000000008e1c typeinfo for ModuleFoo
0x0000000000008e28 vtable for ModuleFoo
Указатель просто не установлен. Единственное различие, которое я вижу между этими двумя версиями является то, что в первых модули инстанциированы в стеке, в то время как на втором они инстанциированы во всем мире в ПБСЕ:
.bss 0x00000000200015fc 0x22c config.o
0x00000000200015fc foo
0x000000002000164c bar
Может ли это будет проблемой?
В любом случае, спасибо за то, что нашли время, чтобы прочитать это!
** EDIT: ** Проблема возникла из кода запуска и сценария компоновщика. Я использовал образцы файлов, поставляемых с инструментальной сеткой ARM GCC от Atmel, которые, как представляется, плохо написаны и, что самое важное, не вызывали __libc_init_array()
(который используется для вызова глобальных конструкторов). Я переключился на использование сценария запуска/компоновщика из ASF, и он работает лучше. Спасибо @FreddieChopin!
Я использую код запуска ASM, предоставленный производителем. Возможно, он слишком длинный, чтобы опубликовать его здесь, но насколько я вижу, он этого не делает, поэтому вы, вероятно, правы. Однако я понятия не имею, как «включить глобальные конструкторы», и я не знаю, какой заголовок включать, чтобы получить доступ к __libc_init_array: «ошибка:« __libc_init_array »не была объявлена в этой области» – Foaly
@Foaly - проверьте мои измененные ответ. –
Спасибо. Он компилируется (мне пришлось переместить проротип за пределы основного), но он не запускается. Вот обратная линия, которая странная, потому что она не начинается с main() ...? # 0 UsageFault_Handler() в ./startup_ARMCM4.S:235 # 1 <обработчика сигнала называется> # 2 0x00008d92 в TypeInfo имя для модуля() # 3 0x00008b64 в __libc_init_array() # 4 0x200004b0 в _impure_ptr() Backtrace остановлен: предыдущий кадр идентичен этому кадру (поврежденный стек?) – Foaly