2017-02-16 2 views
1

Я использую 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!

ответ

2

Покажите нам код запуска, который вы используете. Скорее всего, вы не включили глобальные конструкторы, что можно сделать, вызвав функцию __libc_init_array(). Вы можете проверить эту теорию, вручную вызвав эту функцию в начале main() - тогда она должна работать нормально. Если это так, то вы должны добавить эту функцию к своему коду запуска (Reset_Handler).

Быстрый тест:

int main() { 
    extern "C" void __libc_init_array(); 
    __libc_init_array(); 
    // rest of your code... 

Чтобы сделать это правильно, найти место, где ваш код запуска вызовов основной() (обычно STH как ldr rX, =main и blx rX или, может быть непосредственно как bl main) и прямо перед тем, что делать именно но с __libc_init_array вместо main.

+0

Я использую код запуска ASM, предоставленный производителем. Возможно, он слишком длинный, чтобы опубликовать его здесь, но насколько я вижу, он этого не делает, поэтому вы, вероятно, правы. Однако я понятия не имею, как «включить глобальные конструкторы», и я не знаю, какой заголовок включать, чтобы получить доступ к __libc_init_array: «ошибка:« __libc_init_array »не была объявлена ​​в этой области» – Foaly

+1

@Foaly - проверьте мои измененные ответ. –

+0

Спасибо. Он компилируется (мне пришлось переместить проротип за пределы основного), но он не запускается. Вот обратная линия, которая странная, потому что она не начинается с main() ...? # 0 UsageFault_Handler() в ./startup_ARMCM4.S:235 # 1 <обработчика сигнала называется> # 2 0x00008d92 в TypeInfo имя для модуля() # 3 0x00008b64 в __libc_init_array() # 4 0x200004b0 в _impure_ptr() Backtrace остановлен: предыдущий кадр идентичен этому кадру (поврежденный стек?) – Foaly

Смежные вопросы