I recently spent a lot of time solving a problem with an interrupt-handler, and even though I have solved it, I would like to better understand why things went wrong. (It was related to a UART1 interrupt handler, so I am reducing the code fragments to only that one, but the problem is not related to this specific peripheral).
g_pfnVectors:
[...some lines removed]
.word USART1_IRQHandler
[...more lines removed]
but a bit further down, there is also
.weak USART1_IRQHandler
.thumb_set USART1_IRQHandler,Default_Handler
In the file stm32wlxx_it.cpp
I had implemented the interrupt handler
void USART1_IRQHandler(void) {
uint32_t isrflags = USART1->ISR;
if (isrflags & uart1::rdrNotEmpty) {
uart1::rxNotEmpty();
}
if (isrflags & uart1::tdrEmpty) {
uart1::txEmpty();
}
}
but I forgot to also add a declaration to stm32wlxx_it.h
When building the project, there were no compile or linker errors, but during execution, when the interrupt fired, the code would jump to Default_Handler:
which is also defined in the startup file
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
Question : So if there is an implementation of the interrupt handler, but no declaration, why does the linker falls back to Default_Handler ?
Ok, so the reason is the name mangling of C++.
As my interrupt handlers use C++, I needed to rename the stm32wlxx_it.c
(generated by Stm32CubeMx) file to stm32wlxx_it.cpp
. This invokes the C++ compiler, but also mangles the names of the functions. Doing nm stm32wlxx_it.o
revealed that the actual name of function in the object file is _Z17USART1_IRQHandlerv
.
When you declare the function in stm32wlxx_it.h
it is no longer mangled : if you create the project as C++ project in STM32CubeMx, it will automatically add extern "C"
around the declarations.