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

Bug #1747966 reported by GLX
86
This bug affects 15 people
Affects Status Importance Assigned to Milestone
GNU Arm Embedded Toolchain
Confirmed
Undecided
Unassigned

Bug Description

Hi,
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:

 .text.Default_Handler
                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
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...

Revision history for this message
Tejas Belagod (belagod-tejas) wrote :

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

Revision history for this message
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

Revision history for this message
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:
Sections:
Idx Name Size VMA LMA File off Algn
  0 .isr_vector 00000188 08000000 08000000 00010000 2**0
                  CONTENTS, ALLOC, LOAD, READONLY, DATA

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.

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
Dominik Vogel (windoze) wrote :

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

Revision history for this message
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.
  ******************************************************************************

LoopForever:
    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
Default_Handler:
Infinite_Loop:
 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

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

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) wrote :

Hi GLX,

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

Best regards.

Revision history for this message
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
Default_Handler:
Infinite_Loop:
 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.

Revision history for this message
Bernd Kreuss (prof7bit) wrote :

This is the same bug:
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=83967

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
https://www.mikrocontroller.net/topic/443262#5289786
(see file KL05_demo_gcc_weak_strong_bug.tar.gz and instructions in that posting)

Revision history for this message
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:

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

to:
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.

Revision history for this message
GLX (rayer) wrote :

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

Revision history for this message
Dominik Vogel (windoze) wrote :

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

Revision history for this message
Maxim Kuvyrkov (maxim-kuvyrkov) wrote :

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
Revision history for this message
Thomas Preud'homme (thomas-preudhomme) wrote :

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
Revision history for this message
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

Revision history for this message
Lucas Hutchinson (lucas-hutchinson) wrote :

Hi All,

I am still experiencing this bug on both:
7-2017-q4-major
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

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) wrote :

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.

Revision history for this message
Lucas Hutchinson (lucas-hutchinson) wrote :

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
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

Hope this helps.

Revision history for this message
Lucas Hutchinson (lucas-hutchinson) wrote :

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

Revision history for this message
Thomas Preud'homme (thomas-preudhomme) wrote :

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
Revision history for this message
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.

Revision history for this message
Joshua Booth (xenoamor) wrote :

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

Revision history for this message
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

Revision history for this message
Tarjei Knapstad (tarjei-knapstad) wrote :

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.

Revision history for this message
Gergely Budai (gbudai76) wrote :

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

Gergely Budai (gbudai76)
Changed in gcc-arm-embedded:
status: New → Confirmed
Revision history for this message
Joshua Booth (xenoamor) wrote :

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

Revision history for this message
GLX (rayer) wrote :

Yes, the bug is still there :\

Revision history for this message
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.

Revision history for this message
Bearsh (bearsh) wrote :

9-2019-q4: bug is still present.

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

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
Alex (desertkun) wrote :

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

Revision history for this message
emblocks (gnugcc) wrote :

Well the same happens with 9.3 and without lto.
Somehow the ISR's are not filled in by the linker and are kept as the weak defaults.
I really don't know.

Revision history for this message
GLX (rayer) wrote :

Hm, nobody use ISRs to findout and fix it? Or what special keyword to use in declaration?

Revision history for this message
GLX (rayer) wrote :

I did a quick test on 2020-q2 and it behaves same as 2019-q4 in my case
(with order
$(NAME)-$(CORE).axf: $(STARTUP) $(OBJS)
it works with lto enabled)

Revision history for this message
Christophe Lyon (christophe-lyon) wrote :

There is a fix in upstream binutils:
https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=0e6a3f07f50723d1831291492b96fdf74bcbdc11

It is part of binutils release 2.35, but arm toolchain 2020-q2 still uses version 2.34.50.20200226, so this explains why it still shows the bug.

Revision history for this message
GLX (rayer) wrote :

Good news, hope it will be updated soon. I'm not familiar with gcc/binutils compiling, if someone elese will recompile it let us know, please.

Revision history for this message
falstaff (falstaff) wrote :

I experienced a similar case when linking against libopencm3. Since libopencm3 has lots of symbols just reordering the library in front of the obj files did lead to lots of linker errors. In this case adding a linker group helped: `-Wl,--start-group -lopencm3_stm32f0 $(OBJS) -Wl,--end-group`. However, ordering the library before the obj files is still needed despite the linker group.

Revision history for this message
Mathew Brown (rwx-dev) wrote :

Compiling and linking with -flto works for me with GNU Arm Embedded Toolchain 10-2020-q4-major and 9-2020-q2-update if I use:
  'arm-none-eabi-gcc-ar' instead of 'arm-none-eabi-ar'
  'arm-none-eabi-gcc-nm' instead of 'arm-none-eabi-nm'
  'arm-none-eabi-gcc-ranlib' instead of 'arm-none-eabi-ranlib'

See: https://stackoverflow.com/questions/25878407/how-can-i-use-lto-with-static-libraries/25878408#25878408

Revision history for this message
emblocks (gnugcc) wrote :

I have the same problem with 9.3.
LTO is removing ISR code and the used attribute is not working.

The only why I can make this work is changing the build order which is a very bad workaround of course.

Revision history for this message
emblocks (gnugcc) wrote :

Tip: place a bkpt in the default ISR handler. During debug you will be notified right away, during production, watchdog will kick in.

Revision history for this message
David Hughes (d2865) wrote :

Hi, I've just been battling this with a SAMD10 compiling with arm-none-eabi-gcc (15:10.3-2021.07-4) 10.3.1 20210621 (release). The __attribute__ ((interupt)) or reordering the object files didn't work for me, but I did notice that in the Microchip (Atmel) software the interrupt table itself was not being included. Even though this was declared const, I poked a "volatile" in front, and hey-presto!

For reference, the chip was ATSAMD10D13AM, offending file was startup_samd10.c:

/* Exception Table */
__attribute__ ((section(".vectors")))
volatile const DeviceVectors exception_table = {
...

It seemed to me that as this kept turning up in subsequent versions of GCC, it was unlikely to be a bug in it. I hope this helps!

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

Other bug subscribers

Remote bug watches

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