-flto in GCC 7.2.1 cause ISR is removed from result binary (change from GCC 6.3.1)

Bug #1747966 reported by GLX on 2018-02-07
This bug affects 5 people
Affects Status Importance Assigned to Milestone
GNU Arm Embedded Toolchain

Bug Description

I just upgraded GCC (win32 host) toolchain from ver 6.3.1 to 7.2.1 and found that my recompiled project with -flto is not working. I found this is because new GCC thrown away my ISRs code and leave them directed to the default interrupt handler that cause MCU lokckup.
So here are my gcc compile options:
arm-none-eabi-gcc -mthumb -mcpu=cortex-m4 -DSTM32L476xx -I../../CMSIS/CORE/INCLUDE -Wall -Os -flto -ffunction-sections -fdata-sections -g -c -o main.o main.c

arm-none-eabi-gcc -mthumb -mcpu=cortex-m4 -DSTM32L476xx -I../../CMSIS/CORE/INCLUDE -Wall -Os -flto -ffunction-sections -fdata-sections -g --specs=nano.specs --specs=nosys.specs -L. -L../../ldscripts -T STM32L476RGTx_FLASH.ld -Wl,--gc-sections -Wl,-Map=BLINK-CM4.map -o BLINK-CM4.axf main.o system.o uart.o timer.o spi.o display.o ../../startup/startup_stm32l476xx.s arm-none-eabi-size BLINK-CM4.axf

I tried to assign my ISR to .isr_vector section that is defined in my generic linker script
void __attribute__ ((section(".isr_vector"), used)) USART2_IRQHandler(void)

  .isr_vector :
    . = ALIGN(8);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(8);
  } >FLASH

and I also tried to add KEEP(*(.isr_vector)) to .text section but all this efforts and attributes that I tried doen's have any effect. The LST file doesn't contain ANY reference to USART2_IRQHandler and the MAP file shows the USART2_IRQHandler is assigned to .text.Default_Handler group instead of my code:

                0x08000fec 0x2 C:\DOCUME~1\rehak\LOCALS~1\Temp\ccOwWxwF.o
                0x08000fec RTC_Alarm_IRQHandler
                0x08000fec EXTI2_IRQHandler
                0x08000fec USART2_IRQHandler
                0x08000fec DFSDM1_FLT0_IRQHandler

Even if I call the ISR function directly from main() it's discarded too. Is there any other way how to tell GCC 7 to keep it? Again, in GCC 6 it worked fine without any attributes.

The last though directed me to ISR names definitions. It's the part of STM Cube library, the file: startup_stm32l476xx.s where it is defined like this:

     .section .isr_vector,"a",%progbits
    .type g_pfnVectors, %object
    .size g_pfnVectors, .-g_pfnVectors
    .word _estack
    .word Reset_Handler
    .word USART2_IRQHandler
    .weak USART2_IRQHandler
    .thumb_set USART2_IRQHandler,Default_Handler

Is it OK? No problem with weak or so? When I rename my ISR to some other name that is NOT listed in this startup file it is NOT discarded so I guess there might be some link but I don't understand GAS asm...

Tejas Belagod (belagod-tejas) wrote :

Thanks for the report. Is the isr section kept if you drop the -flto?

Joey Ye (jinyun-ye) wrote :

Thanks for report. Can you please confirm if the same project works on GCC6 without modification? If yes, it looks a regression or behaviour change in GCC7

GLX (rayer) wrote :

Hi, sure if I remove -flto option the ISRs are not dropped from binary even without use of any mentioned attributes. But the section itself is listed in LST file:
Idx Name Size VMA LMA File off Algn
  0 .isr_vector 00000188 08000000 08000000 00010000 2**0

Yes, the project compiled fine with GCC 6.3.1 and 5.x that I use before. I discovered the problem with upgrading to 7.2.1. If there's some other GCC 7.x or 8.x available I could try.

Dominik Vogel (windoze) wrote :

Got exactly the same problem.
My project doesn't have any interrupt vectors assigned in the resulting binary.
Using "-fno-lto" the project is built correctly but I loose the benefits of LTO.
With GCC v6 the LTO is working fine with the same code / project.

GLX (rayer) wrote :

>Dominik Vogel
Hi, good that it's confirmed by other person, maybe it will get more attention. BTW do you include any STM32 header files for ISR definitions? My project is for STM32L476 and I use only basic Cube headers with registers definitions but not any c file.

Dominik Vogel (windoze) wrote :

Same here. I'm using an STM32F103 and only use the Register definitions from the Cube-Library.

GLX (rayer) wrote :

OK, it might be possible that only STM32 header users suffer this problem. Can someone else confirm that GCC is not throwing his ISR with LTO and post some details about his ISR definitons?

Here's a part of STM32 startup file that difines ISR aliases. Can somebody check if it doesn't violates any GCC/GAS standard that could cause this bug?

  * @file startup_stm32l476xx.s
  * @author MCD Application Team
  * @version V1.3.1
  * @date 21-April-2017
  * @brief STM32L476xx devices vector table GCC toolchain.
  * This module performs:
  * - Set the initial SP
  * - Set the initial PC == Reset_Handler,
  * - Set the vector table entries with the exceptions ISR address,
  * - Configure the clock system
  * - Branches to main in the C library (which eventually
  * calls main()).
  * After Reset the Cortex-M4 processor is in Thread mode,
  * priority is Privileged, and the Stack is set to Main.

    b LoopForever

.size Reset_Handler, .-Reset_Handler

 * @brief This is the code that gets called when the processor receives an
 * unexpected interrupt. This simply enters an infinite loop, preserving
 * the system state for examination by a debugger.
 * @param None
 * @retval : None
    .section .text.Default_Handler,"ax",%progbits
 b Infinite_Loop
 .size Default_Handler, .-Default_Handler
* The minimal vector table for a Cortex-M4. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
  .section .isr_vector,"a",%progbits
 .type g_pfnVectors, %object
 .size g_pfnVectors, .-g_pfnVectors

 .word _estack
 .word Reset_Handler
 .word NMI_Handler
 .word HardFault_Handler
 .word MemManage_Handler
 .word BusFault_Handler
 .word UsageFault_Handler
 .word 0

* Provide weak aliases for each Exception handler to the Default_Handler.
* As they are weak aliases, any function with the same name will override
* this definition.

  .weak NMI_Handler
 .thumb_set NMI_Handler,Default_Handler

  .weak HardFault_Handler
 .thumb_set HardFault_Handler,Default_Handler

  .weak MemManage_Handler
 .thumb_set MemManage_Handler,Default_Handler

  .weak BusFault_Handler
 .thumb_set BusFault_Handler,Default_Handler

 .weak UsageFault_Handler
 .thumb_set UsageFault_Handler,Default_Handler


What happen if you make the handlers global instead of weak?

Best regards.

GLX (rayer) wrote :

OK, i tried it and the only change is that some new lines appeared in LST file:

08000fec <USART2_IRQHandler>:
 * @retval : None
    .section .text.Default_Handler,"ax",%progbits
 b Infinite_Loop
 8000fec: e7fe b.n 8000fec <USART2_IRQHandler>

but in MAP file it is still directed to default infinite loop handler and also the resulting BIN, HEX files was not changed.

Bernd Kreuss (prof7bit) wrote :

This is the same bug:

It can be worked around by changing the linking order, startup code .o file must appear earlier on the command line than .o files containing the strong symbols.

Here is a minimal self contained example
(see file KL05_demo_gcc_weak_strong_bug.tar.gz and instructions in that posting)

GLX (rayer) wrote :

Hi Bernd,
thank you to let us know. I can confirm that your workaround also works for me.
I simply changed a line in my Makefile:

$(NAME)-$(CORE).axf: $(STARTUP) $(OBJS)

all: $(NAME)-$(CORE).axf lst text
$(NAME)-$(CORE).axf: $(STARTUP) $(OBJS)
 $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
 $(SIZE) $(NAME)-$(CORE).axf

and the binary now includes code of ISR. I don't need to mark ISR code with any extra attributes. S I would wait for some future build when it will be fixed.

GLX (rayer) wrote :

Sorry, I meant
$(NAME)-$(CORE).axf: $(OBJS) $(STARTUP)
$(NAME)-$(CORE).axf: $(STARTUP) $(OBJS)

Dominik Vogel (windoze) wrote :

Works for me too! Thanks for the hint / workaround Bernd.

Hi Tejas,

As discussed in https://sourceware.org/bugzilla/show_bug.cgi?id=22502 this likely was fixed in binutils 2.30. Do you have a GCC7 toolchain with newer binutils to test?

Changed in gcc-arm-embedded:
milestone: none → 7-2018-q2-update
no longer affects: gcc-arm-embedded/7.0
Changed in gcc-arm-embedded:
status: New → Fix Released

As mentioned by Maxim, this should be fixed in 7-2017Q4 release. Can someone confirm?

Changed in gcc-arm-embedded:
milestone: 7-2018-q2-update → 4.9-2015-q3-update
milestone: 4.9-2015-q3-update → 7-2018-q2-update
milestone: 7-2018-q2-update → none
Changed in gcc-arm-embedded:
milestone: none → 7-2017-q4-major
mrvanes (mrvanes) wrote :

I was experiencing this bug as well and can add that gcc-arm-none-eabi-7-2017-q4-major from https://developer.arm.com/open-source/gnu-toolchain/gnu-rm/downloads still exhibits this problem.
I needed to put startup before sources for -flto to work, thanks for the work-around!

I can't test with Ubuntu 18.04 packages because of this bug https://bugs.launchpad.net/ubuntu/+source/newlib/+bug/1768125

To post a comment you must log in.
This report contains Public information  Edit
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.