5

My target device is an EFM32 Cortex-M3 based device. My toolchain is the official ARM GNU toolchain gcc-arm-none-eabi-8-2018-q4-major.

Everything works fine without LTO, but to make LTO work I have to mark all interrupt handler code with -fno-lto. I would like to get rid of this workaround.

The problem is, every interrupt handler is getting removed from the final binary. (I am checking with arm-none-eabi-nm --print-size --size-sort --radix=d -C -n file.out) This makes the resulting binary crash.

Digging deeper and after googling for similar problems:

Sample code from startup_efm32gg.c defines default interrupt handlers as such:

void DMA_IRQHandler(void) __attribute__ ((weak, alias("Default_Handler")));
/* many other interrupts */
void Default_Handler(void) { while (1); }

The same problem happens for regular interrupt handler definitions as well (as in, no aliases and not weak)

It might be related but it seems that weak symbols are misbehaving in LTO mode in the same way.

Thank you in advance for any ideas!

Edit: See my reply to the marked answer for a full solution!

RushPL
  • 4,732
  • 1
  • 34
  • 44

1 Answers1

3

Where are your interrupt handlers referenced from? Just like unreferenced static functions and objects will be removed from a single translation unit, external ones that are unused will be removed during LTO. In order to prevent this (and in order for your program to be valid anyway in the abstract model) there needs to be some chain of references, starting from the entry point, leading to the functions and objects; if none exists, then you're not actually using them in your program.

If the reference is from a linker script or asm source file, it's possible that this is a bug in LTO, and it's not seeing the references like it should. In this case you might be able to apply a hack like __attribute__((__used__)) to the affected function definitions. Alternatively you could make fake references to them, e.g. by storing their addresses to dummy volatile objects or using their addresses in input constraints to empty inline asm blocks. As yet another alternative, there may be a way to redo whatever you're doing with asm source files or linked scripts to make your interrupt table at the C level, with appropriate structs/arrays in special sections, so that the compiler can actually see the references without you having to fake them.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • Thank you .. you have raised some good questions. The exception handlers are referenced in the same file that defines the default handlers: `const pFunc __Vectors[] __attribute__ ((section(".vectors"))) = { /* Cortex-M Exception Handlers */ (pFunc)&__StackTop, /* Initial Stack Pointer */ Reset_Handler, /* Reset Handler */ NMI_Handler, ` I will try marking this symbol as used and I'll let you know! But for the handlers themselves .. marking them as used does not help. – RushPL Jan 01 '19 at 17:20
  • 1
    @RushPL: OK, then where is `__Vectors` referenced from? I suspect this is your problem. – R.. GitHub STOP HELPING ICE Jan 01 '19 at 18:29
  • Thanks! Vectors is being referenced only by the linker script. I spent the last hour trying different things and indeed marking the `__Vectors` symbol with the `used` attribute, helps. It wasn't the whole solution though! Other important takeaway is that weak functions and their overrides should be compiled in the same way (either all LTO, or all no LTO) Having a basic weak implementation compiled with -fno-lto and its override in LTO (or vice versa) was causing the override to not work. In the end .. almost all of my code is compiling with LTO! Newlib overrides being the only exception. – RushPL Jan 01 '19 at 18:54
  • @RushPL: I think you're mistaken about weak and the mechanism of your problem is something different. Strong definitions, *if linked at all*, replace weak definitions before any garbage collection of unreferenced code can happen; if this were not the case, LTO would break semantics badly and would be unusable for anything using weak definitions. Perhaps your strong definitions are in object files inside a `.a` library and nothing causes those files to be pulled into the link at all? Maybe `--whole-archive` is broken with LTO? – R.. GitHub STOP HELPING ICE Jan 01 '19 at 19:24
  • You may be right R.., I will try to find out what's going on and I'll keep this thread updated. Thanks again for your help. – RushPL Jan 02 '19 at 10:51
  • I am using the same compiler as the OP and I am in the lucky position to have two projects: A preliminary sketch and a restructured version. The old version links with LTO, the new version does not. So, arm_none_eabi_gcc 8-2018-q4-major **can** do LTO linking. Sometimes. I can swap the `startup_stm32l476xx.o` files between the projects (source is identical, binary differs by some bytes) and the behaviour does not change. `nm` and `readelf` did not show differences. I have a workaround, though. Any idea how I can isolate the problem? – hermannk Jan 20 '19 at 15:26
  • @hermannk you probably have an interrupt handler somewhere that you haven't marked as used. Hunt them down, there can't be that many. :) I was able to get my projects working with LTO by marking the vector table and all interrupt handlers as used. – escrafford Mar 08 '19 at 06:33
  • I was experiencing the same problem as described in the original question. In my case the problem seemed to be dependent on the order in which object files were passed into the linker (arm-none-eabi-gcc). I have two relevant source files: `gcc_startup_nrf52.S` holding the default IRQ handler and `myirq.c` holding my custom IRQ handler, both compiled with `-flto`. When calling the linker like `arm-none-eabi-gcc ... gcc_startup_nrf52.o myirq.o`, everything worked as expected, but when calling like `arm-none-eabi-gcc ... myirq.o gcc_startup_nrf52.o` I observed the same issues. – MarkusM Aug 05 '20 at 07:56