Is it safe to strip .weak/MSP weak bodies so a static-lib archive pulls in real IRQ/MSP overrides?
Setup:
I have a CubeMX/CubeIDE-generated STM32G474RET6 project (ThreadX) that I build as a static archive rather than a flashable executable, meant to be linked into a separate top-level consumer application. The consumer supplies main(), calls into this library's init function, and never defines its own vector table, interrupt handlers, or MSP callbacks — this library owns all of that.
Problem:
A static archive only pulls in the .o members something already-linked actually references (need-driven extraction). That interacts badly with two CubeMX-generated weak-symbol patterns:
-
Vector table vs.
stm32g4xx_it.c:startup_stm32g474retx.sgives every handler a.weak/.thumb_set XXX,Default_Handlerfallback, satisfied locally from the always-linked startup object. Since nothing else references handlers likeNMI_Handleror a peripheral'sIRQHandler, the linker never has a reason to pullstm32g4xx_it.c.oout of the archive — the real handlers in it are silently dropped in favor ofDefault_Handler. Build and flash both succeed; nothing interrupt-driven actually runs. -
HAL MSP callbacks: Same problem, different shape. e.g.
stm32g4xx_hal_adc.cboth callsHAL_ADC_MspInit()and defines a__weakempty-body fallback for it in the same translation unit. The moment something pullsstm32g4xx_hal_adc.c.oin (e.g. a call toHAL_ADC_Init()), the weak symbol is already satisfied from that same object — so the linker never has a reason to reach into the archive for my ownstm32g4xx_hal_msp.c.o, and the real MSP override (clock/GPIO/DMA/NVIC config) is silently dropped too. This applies to any peripheral whose MSP callback is overridden this way — ADC, TIM, I2C, SPI, USART, etc.
What I did:
- In
startup_stm32g474retx.s, removed the.weak/.thumb_set XXX,Default_Handlerpair for every vector thatstm32g4xx_it.cactually implements (left the.wordentry ing_pfnVectorsuntouched). This makes those names genuinely undefined untilstm32g4xx_it.c.ois pulled in to resolve them. - In the relevant
Drivers/STM32G4xx_HAL_Driver/Src/stm32g4xx_hal_*.cfiles, wrapped the weak MSP fallback body in#if 0(keepingUNUSED(...)outside it) for eachHAL_xxx_MspInit/MspDeInitpair that my ownstm32g4xx_hal_msp.cactually overrides. This makes the call site an undefined reference until my realstm32g4xx_hal_msp.c.ois extracted.
Both changes are pure removals of a weak fallback — the strong definitions and the vector table's .word entries are untouched.
Questions:
- Is this a safe approach, or is there an ST-supported mechanism for forcing specific objects out of a vendor-generated static archive (an official "anchor"/
KEEP-equivalent pattern) instead of hand-editing the CubeMX-generated startup file and HAL sources? - Any caveats I should know about re: CubeMX re-generation or a STM32Cube firmware package upgrade silently reintroducing the weak fallback and masking this again with no build error?
- Any correctness concerns specific to ThreadX or the G4's NVIC/vector table setup with this approach that I might be missing?
