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 (в основном для того, чтобы попробовать, на самом деле это не нужно). Но при компиляции с -fltog++ игнорирует мою реализацию обработчика и просто использует обработчик по умолчанию, мой обработчик вообще отсутствует в коде.

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

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

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

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

1 ответ

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

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

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

Тем не менее, для тех, кто ищет это, в 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

к

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

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

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

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

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

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

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

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

Другие вопросы по тегам