LTO misscompilation

I’ve been hunting some LTO issues in our code and this is some of the weirdness. Its not about platformio specifically but maybe someone has some good ideas.

The main issue I have been looking at is that a weak definition of a function gets used instead of the strong one when i enable LTO.

I was trying to reduce an example of this when I ran into this that might be related. When I enable LTO in this example I can compile 2 strong definitions without issue. Without LTO GCC correctly gives an error.

Somewhat small reproduction:

platformio.ini:

[env:default]
platform = ststm32
board = hy_tinystm103tb

build_flags =
  -Wl,-Map,"$BUILD_DIR/${PROGNAME}.map"
  ; what?: using -flto allows multiple strong definitions
  -flto

src/main.c

int main(int argc, char* argv[]) {
  (void)argc;
  (void)argv;
  while (1);
}

__attribute__ ((used))  void NMI_Handler(void){}
// what?: removing used makes flto error correctly
// void NMI_Handler(void){}

void _exit(int code) { (void)code; while (1);}

src/strong.c
void NMI_Handler(void){}

src/startup.S

.global g_pfnVectors

Default_Handler:
Infinite_Loop:
  b Infinite_Loop
  .size Default_Handler, .-Default_Handler

g_pfnVectors:
  .word  NMI_Handler

.weak      NMI_Handler
.thumb_set NMI_Handler,Default_Handler

Some links that might be related:
謝憲譁 - Linker doesn’t replace weak functions in assembly with global function i (gnu.org)
Linker doesn’t replace weak functions in assembly with global function in C when LTO is turned on · Issue #172 · riscvarchive/riscv-gcc (github.com)