Как и обещал, сделаю развернутое пояснение. Для начала стоит
обратить внимание на выхлоп компилятора, для чего скормим файл
прошивки программе readelf (в качестве примера я взял пример blink
из своей либины): $readelf blink.elf -a
<...>
Заголовки разделов:
[Нм] Имя Тип Адрес Смещ Разм ES Флг Сс Инф Al
[ 0] NULL 00000000 000000 000000 00 0 0 0
[ 1] .isr_flash PROGBITS 08000000 010000 000188 00 A 0 0 4
[ 2] .text PROGBITS 08000188 010188 0004cc 00 AX 0 0 4
[ 3] .rodata PROGBITS 08000654 010654 000004 00 A 0 0 4
[ 4] .data PROGBITS 20000000 020000 0000d8 00 WA 0 0 4
[ 5] .ccmram PROGBITS 10000000 0200d8 000000 00 W 0 0 1
[ 6] .ARM ARM_EXIDX 08000658 010658 000008 00 AL 2 0 4
[ 7] .init_array INIT_ARRAY 08000660 010660 000004 04 WA 0 0 4
[ 8] .fini_array FINI_ARRAY 08000664 010664 000004 04 WA 0 0 4
[ 9] .bss NOBITS 200000d8 0200d8 0000dc 00 WA 0 0 4
[10] ._user_heap_stack NOBITS 200001b4 0200d8 000604 00 WA 0 0 1
Сразу увидим список секций/разделов, в данном случае список сокращен. В секции text покоится наша логика, rodata - константы, data - область с инициализируемыми данными, bss - область для инициализации 0, [init_array - fini_array] - отсюда копируются данные в секцию data. Заглянем в стартап, именно он занимается инициализацией наших переменных, а также вызывает конструкторы классов (в том числе и в голом С):
[noreturn]] void ResetHandler() {
__set_MSP(reinterpret_cast<std::uint32_t>(&__initial_sp__));
// Relocate the .data section
std::uint32_t* dl = & __data_load__;
std::uint32_t* ds = & __data_start__;
std::uint32_t* de = & __data_end__;
if (dl != ds) {
while(ds < de) {
*ds = *dl;
dl++;
ds++;
}
}
// Initiailize .bss to zero
std::uint32_t* bs = & __bss_start__;
std::uint32_t* be = & __bss_end__;
while (bs < be) {
*bs = 0;
bs++;
}
board::Init();
// Call static constructors
__libc_init_array();
main();
while(1) __NOP();
}
Я считаю коментарии в коде исчерпывающими. Теперь посмотрим, что будет, если мы добавим в код ваш массив, а будет плохо:
warning: 'at' attribute directive ignored [-Wattributes]
GCC не умеет размещать переменные по конкретному адресу, это вообще непонятный, грязный и максимально убогий хак. Зачем это существует в "профессиональных" для меня загадка, необходимо явно задать секцию, в которую будет помещен массив:
__attribute__ ((section(".isr_flash"), used)) void (* const InterruptTable[])(void)
Именно таким образом размещаются таблицы векторов прерываний. Но разместив что-то в одной секции, условно "my_section", это что-то никогда не попадет в секцию "bss", даже если оно инициализируется 0. Если мы внимательно посмотрим в код ResetHandler - переменные, размещенные таким образом в RAM вообще не будут инициализмрованы! Переменные во FLASH в данном случае являются исключением - они будут прошиты по тому адресу, с которого читаются.
Вам необходимо явно сделать обнуление данных вашей секции в функции ResetHandler по аналогии с обнулением bss.