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