Skip to main content
macar
Associate II
July 27, 2026
Question

Is it safe to strip .weak/MSP weak bodies so a static-lib archive pulls in real IRQ/MSP overrides?

  • July 27, 2026
  • 1 reply
  • 82 views

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:

  1. Vector table vs. stm32g4xx_it.c: startup_stm32g474retx.s gives every handler a .weak/.thumb_set XXX,Default_Handler fallback, satisfied locally from the always-linked startup object. Since nothing else references handlers like NMI_Handler or a peripheral's IRQHandler, the linker never has a reason to pull stm32g4xx_it.c.o out of the archive — the real handlers in it are silently dropped in favor of Default_Handler. Build and flash both succeed; nothing interrupt-driven actually runs.

  2. HAL MSP callbacks: Same problem, different shape. e.g. stm32g4xx_hal_adc.c both calls HAL_ADC_MspInit() and defines a __weak empty-body fallback for it in the same translation unit. The moment something pulls stm32g4xx_hal_adc.c.o in (e.g. a call to HAL_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 own stm32g4xx_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_Handler pair for every vector that stm32g4xx_it.c actually implements (left the .word entry in g_pfnVectors untouched). This makes those names genuinely undefined until stm32g4xx_it.c.o is pulled in to resolve them.
  • In the relevant Drivers/STM32G4xx_HAL_Driver/Src/stm32g4xx_hal_*.c files, wrapped the weak MSP fallback body in #if 0 (keeping UNUSED(...) outside it) for each HAL_xxx_MspInit/MspDeInit pair that my own stm32g4xx_hal_msp.c actually overrides. This makes the call site an undefined reference until my real stm32g4xx_hal_msp.c.o is extracted.

Both changes are pure removals of a weak fallback — the strong definitions and the vector table's .word entries are untouched.

Questions:

  1. 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?
  2. 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?
  3. Any correctness concerns specific to ThreadX or the G4's NVIC/vector table setup with this approach that I might be missing?




 

1 reply

Pavel A.
July 30, 2026

There is no special “ST-supported mechanism”, just what is provided with the toolchain (GNU, or IAR if you can afford that). So whatever works with the standard GCC & linker, is good.

> Any caveats I should know about re: CubeMX re-generation

CubeMX re-generation usually does not touch the linker scripts (.ld) since they are created. 

Other .c files may be clobbered by re-generation, so use a version control to track all your source files, detect unwanted changes and revert  them.