简体   繁体   中英

arm-none-eabi-g++ does not correctly handle weak alias with -flto

I am programming an STM32F413 microcontroller with SystemWorkbench 4 stm32. The Interrupt vectors are defined in an assembly startup file as weak aliases like follows:

.weak   TIM1_UP_TIM10_IRQHandler
.thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler

And referenced in an object like follows:

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

So that the g_pfnVectors is a list of the addresses of the IRQ Handler functions. They are declared as weak aliases, so that if they are not defined by the user, the default handler is used.

I have defined the handler like this:

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

This works fine with the normal compiler optimization flags, however I wanted to try if I get smaller and possibly faster code with -flto (mainly for trying it, don't really needed it). But when compiling with -flto , g++ ignores my implementation of the handler and just uses the default handler, my handler isn't in the code at all.

So I tried to force g++ to include the function by adding __attribute__((used)) to the function definition, but it was still not compiled. However if I give it another name, then it was included in the binary. Also if I remove the weak alias and just have a reference to the handler in the startup file, it works too.

So somehow the weak aliases don't work with g++ link time optimization. Maybe someone can tell me what the error is and what I'm doing wrong here.

EDIT:

I have looked at which symbols are created with nm on the resulting .elf File, and the TIM1_UP_TIM10_IRQHandler is exported as a weak symbol with the address of the DefaultHandler. However when viewing just the .o file from the compilation unit containing the TIM1_UP_TIM10_IRQHandler function, it is exported as a symbol in the text section (T). So the linker, for some reason, chooses to keep the weak symbol, even though there is a strong symbol with the same name.

I think you should inform the compiler that it the interrupt __attribute__ ((interrupt ("IRQ"))) , which is not needed normally as F4 has the stack by default aligned to 8 by the hardware.

If it does not help the workaround is to have a function pointer assigned with the handler, which will prevent it from discarding (if the pointer itself will not be discarded itself - check with your debugger).

The last resort - change the .s file with the vector table definitions

For those looking for this, still, there is apparently a confirmed bug in GCC 7 related to link-time optimization ( -flto ):

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

I have just run into this, again, with GCC 8 (gcc-arm-none-eabi-8-2019-q3-update release), the behavior is still the same.

The workaround that also works for me (from https://github.com/ObKo/stm32-cmake/issues/78 ) is to remove or comment the weak definitions at the end of the startup_XXX.s file, so change, for example

    .weak   NMI_Handler
    .thumb_set NMI_Handler,Default_Handler

to

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

and replace them with your own implementation in a source file:

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

All weak handlers need to be removed that are being called, so for example if you have UART1_Handler() defined in the HAL/LL drivers, you need to remove the corresponding .weak entry from the startup_XXX.s file, otherwise the interrupt will lock up the MCU by getting stuck in the default infinite loop, without executing the intended interrupt handler and returning from the interrupt, allowing other code execution to resume.

This bug is still present in gcc-arm-none-eabi-9-2020-q3-update but only for C handlers. Weirdly enough, handlers written in C++ (and declared with extern "C" linkage) are not anymore affected by this bug.

As another workaround, rather than messing with the startup.s file, I found that putting the IRQ handlers in separate .c files and building those (and only those) without LTO does the trick.

For those using CubeIDE and generating IRQ/HAL handlers with CubeMX (aka. "Device Configuration Tool"), all auto-generated handlers are in Core\\Src\\stm32XXXX_it.c , you just have to edit the properties of this file and remove LTO from the compilation options.

This is sub optimal, but it fits well with auto-generated IRQ/HAL handlers: only the first call (from IRQ handler to HAL handler) is unoptimized, but the HAL code itself is correctly optimized.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM