-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 15 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

Hi All,

I am still experiencing this bug on both:
7-2018-q2-update (seems to be called 7-2018-q3-update when installed using the ppa on ubuntu 18.04)

My code is using an STM32f3 with the startup file similar to those mentioned above for similar chips.
I can also confirm that passing this startup assembly file to the linker first causes the resulting LTO build to become functional again.
Let me know if i can help in any way, or need to supply some more information

The version is 7-2018q2-1~bionic1 on Ubuntu 18.04. Where do you see the 7-2018-q3-update? Can you show the output of apt-cache policy gcc-arm-embedded?

Best regards.

Hi Thomas,

this the the output from arm-none-eabi-gcc --version which is where I see the -q3
lucas@lucas-ubuntu:~$ arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU Tools for Arm Embedded Processors 7-2018-q3-update) 7.3.1 20180622 (release) [ARM/embedded-7-branch revision 261907]
Copyright (C) 2017 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO

Hope this helps.

This is the output of apt-cache:
sudo apt-cache policy gcc-arm-embedded
  Installed: 7-2018q2-1~bionic1
  Candidate: 7-2018q2-1~bionic1

Right, the Ubuntu PPA was built during Q3 which is why it shows Q3. That's actually our 7-2018Q2 toolchain as shown by the output of apt-cache. Alright so you still encounter the bug, adjusting the status of this bug accordingly.

Changed in gcc-arm-embedded:
status: Fix Released → New
Morty (morty-rh) wrote :

I have the same issue, but to me it seems even worse using 7-2018-q2-update on windows: Even without LTO the order of the objects is relevant (the weak symbols must be last) and with LTO I can't get the correct symbols linked at all.

Joshua Booth (xenoamor) wrote :

I'm also still seeing this issue on the 7-2018-q2-update version

Artur Troian (troian) wrote :

I have experienced this issue as well and workaround (fix?) is to put libs between -Wl,--whole-archive -Wl,--no-whole-archive during link stage

Still experiencing this issue with 8-2018-q4-major

The -Wl,--whole-archive workaround does not help.

Placing startup.o in front when linking works.

Gergely Budai (gbudai76) wrote :

I can confirm that this is still broken in 8-2018-q4-major.

Gergely Budai (gbudai76) on 2019-03-07
Changed in gcc-arm-embedded:
status: New → Confirmed
Joshua Booth (xenoamor) wrote :

Is this still broken in the 8-2019-q3-update?

GLX (rayer) wrote :

Yes, the bug is still there :\

June Hanabi (junebug12851) wrote :

I happily found an alternative fix, after poking around in the gcc function attributes there exists function attributes that mark the function as an interrupt and theirs many of them depending on which processor your targeting.

If your targeting ARM like I am and like others are in this thread then

`__attribute__ ((interrupt))`

is the attribute for ARM. I just popped that in and all the problems went away. It worked like magic. I was able to apply full optimization along with stripping of everything and de-duplicating, you name it, no problems whatsoever.

With that attribute, as I said above, I didn't have to muck around with ordering of code which may be easier for some but for me it's more difficult given my IDE so I'm glad I didn't have to delve too deeply into that.

Bearsh (bearsh) wrote :

9-2019-q4: bug is still present.

`__attribute__ ((interrupt))` doesn't work for me

GLX (rayer) wrote :

Hm, probably this bug has too low priority that nobody works on fix it as it can be workarounded by parameter order. Nothing to wonder, on avr-gcc the internal compiler push-reload error, that I and other users on avr freeaks encountered, was fixed after 3 years ago when leaping from gcc 4.x to gcc 8.x.

GLX (rayer) wrote :

Sigh, it get even worse. The new GCC 9.2.1 with -lto option did make inline my custom timer_init function some wrongway that my code doesn't work. When I mark this function with __attribute__ ((noinline)) or when I change order of init_xx functions in my main() it gets working. I don't want more bad surprises so I rather reverted back to older GCC.

Alex (desertkun) wrote :

Yeah I was happy at first when I enabled lto only to find out that it stripped out all my ISRs :(

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.