arm-none-eabi-g++ неправильно обрабатывает слабый псевдоним с -flto

Я программирую микроконтроллер STM32F413 с помощью SystemWorkbench 4 stm32. Векторы прерывания определены в файле запуска сборки как слабые псевдонимы, как показано ниже:

.weak   TIM1_UP_TIM10_IRQHandler
.thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler

И упоминается в объекте следующим образом:

g_pfnVectors:
  .word _estack
  .word Reset_Handler
  .word NMI_Handler
  .....
  .word TIM1_UP_TIM10_IRQHandler
  .....

Так что g_pfnVectors это список адресов функций обработчика IRQ. Они объявлены как слабые псевдонимы, поэтому, если они не определены пользователем, используется обработчик по умолчанию.

Я определил обработчик следующим образом:

extern "C" {
void TIM1_UP_TIM10_IRQHandler() {
    if (SU_TIM->SR & TIM_SR_UIF) {
        SU_TIM->SR &= ~TIM_SR_UIF;
        ...
    }
}
}

Это прекрасно работает с обычными флагами оптимизации компилятора, однако я хотел попробовать, если я получу меньший и, возможно, более быстрый код с -flto (в основном для того, чтобы попробовать его, на самом деле это не нужно). Но при компиляции с -flto g++ игнорирует мою реализацию обработчика и просто использует обработчик по умолчанию, моего обработчика вообще нет в коде.

Поэтому я попытался заставить g++ включить функцию, добавив __attribute__((used)) в определение функции, но она все еще не была скомпилирована. Однако, если я дам ему другое имя, тогда он был включен в двоичный файл. Также, если я удалю слабый псевдоним и просто укажу ссылку на обработчик в файле запуска, это тоже сработает.

Так что слабые псевдонимы почему-то не работают с оптимизацией времени ссылки g++. Может быть, кто-нибудь может сказать мне, что это за ошибка и что я делаю неправильно здесь.

РЕДАКТИРОВАТЬ:

Я посмотрел, какие символы создаются с помощью nm в результирующем файле .elf, и TIM1_UP_TIM10_IRQHandler экспортируется как слабый символ с адресом DefaultHandler. Однако при просмотре только файла .o из модуля компиляции, содержащего функцию TIM1_UP_TIM10_IRQHandler, он экспортируется как символ в текстовом разделе (T). Таким образом, компоновщик по какой-то причине предпочитает сохранить слабый символ, даже если есть сильный символ с тем же именем.


person Alexander Daum    schedule 21.08.2018    source источник


Ответы (3)


Я думаю, вы должны сообщить компилятору, что это прерывание __attribute__ ((interrupt ("IRQ"))), которое обычно не требуется, поскольку F4 имеет стек по умолчанию, выровненный до 8 аппаратно.

Если это не помогает, обходной путь состоит в том, чтобы назначить указатель функции с обработчиком, что предотвратит его отбрасывание (если сам указатель не будет отброшен сам - проверьте с помощью отладчика).

В крайнем случае - изменить файл .s с определениями векторной таблицы

person 0___________    schedule 21.08.2018
comment
Атрибут прерывания ничего не меняет, он все равно отбрасывается. Кроме того, назначение указателя функции с помощью обработчика просто загружает указатель функции с адресом DefaultHandler... каким-то образом кажется, что слабый псевдоним работает неправильно. - person Alexander Daum; 21.08.2018
comment
@AlexanderDaum Разве указатель функции не отбрасывается сам по себе - проверьте с помощью отладчика. - person 0___________; 21.08.2018
comment
обычно он отбрасывается, однако я просто попытался вызвать его в основном и посмотреть на полученный код, это был переход к DefaultHandler, что означает, что указатель функции был оптимизирован, но все же разрешен вызов DefaultHander. Я изменил файл .s на данный момент, но все же хотел бы знать, почему вообще возникает проблема. - person Alexander Daum; 21.08.2018
comment
@AlexanderDaum Я думаю, вы должны сообщить об этом. Похоже на ошибку gcc. Вам необходимо внести изменения в файл .s - person 0___________; 21.08.2018

Для тех, кто ищет это, тем не менее, по-видимому, в GCC 7 есть подтвержденная ошибка, связанная с оптимизацией времени компоновки (-flto):

https://bugs.launchpad.net/gcc-arm-embedded/+bug/1747966

Я только что столкнулся с этим, опять же, с GCC 8 (выпуск gcc-arm-none-eabi-8-2019-q3-update), поведение остается прежним.

Обходной путь, который также работает для меня (от https://github.com/ObKo/stm32-cmake/issues/78) — удалить или прокомментировать слабые определения в конце файла startup_XXX.s, поэтому измените, например

    .weak   NMI_Handler
    .thumb_set NMI_Handler,Default_Handler

to

/*
    .weak   NMI_Handler
    .thumb_set NMI_Handler,Default_Handler
*/

и замените их собственной реализацией в исходном файле:

void NMI_Handler(void)
{
    //...
}

Все слабые обработчики, которые вызываются, должны быть удалены, поэтому, например, если у вас есть UART1_Handler(), определенный в драйверах HAL/LL, вам необходимо удалить соответствующую запись .weak из файла startup_XXX.s, иначе прерывание заблокирует MCU, получив застрял в бесконечном цикле по умолчанию, не выполняя намеченный обработчик прерывания и не возвращаясь из прерывания, позволяя возобновить выполнение другого кода.

person dognotdog    schedule 08.08.2019

Эта ошибка все еще присутствует в gcc-arm-none-eabi-9-2020-q3-update, но только для обработчиков C. Как ни странно, обработчики, написанные на C++ (и объявленные с компоновкой extern "C"), больше не подвержены этой ошибке.

В качестве другого обходного пути, вместо того, чтобы возиться с файлом startup.s, я обнаружил, что размещение обработчиков IRQ в отдельных файлах .c и создание их (и только этих) без LTO помогает.

Для тех, кто использует CubeIDE и генерирует обработчики IRQ/HAL с помощью CubeMX (он же Device Configuration Tool), все автоматически сгенерированные обработчики находятся в Core\Src\stm32XXXX_it.c, вам просто нужно отредактировать свойства этого файла и удалить LTO из параметров компиляции.

Это неоптимально, но хорошо сочетается с автоматически сгенерированными обработчиками IRQ/HAL: только первый вызов (от обработчика IRQ к обработчику HAL) неоптимизирован, но сам код HAL оптимизирован корректно.

person syam    schedule 29.11.2020