vTaskSwitchContext() Linker error when link time optimization is enabled (-flto) on gcc and STM32

bob-wss wrote on Thursday, October 03, 2019:

I’m using FreeRTOS V10.1.0 with gcc 6.3.1 on an STM32F427.
When I enable link time optimization (-flto) I get a linker error regarding vTaskSwitchContext():

/tmp/cczXZviN.ltrans3.ltrans.o: In function `PendSV_Handler':
<artificial>:(.text+0x42c): undefined reference to `vTaskSwitchContext'

This is because vTaskSwitchContext() is called from asm, and all places it gets called from in C are optimimized away.
There is a workaround for this in Source/portable/GCC/ARM_CM4F/port.c, vTaskSwitchContext() is called from xPortStartScheduler(), however gcc has become smart enough to detect that his code is never executed, and optimizes the call way.

Therefore I developed a new workaround, add a new function to Source/portable/GCC/ARM_CM4F/port.c (or any other C file if appropriate):

/* 	Call vTaskSwitchContext() from a function with the used attribute set
    so that link time optimisation does not remove the symbol.*/
static void __attribute__((used)) vPortNoOptimize()
{
	vTaskSwitchContext();
}

I hope this helps someone.

rtel wrote on Thursday, October 03, 2019:

Thanks for reporting. Did you try V10.2.1? I think that has some
update to help with link time optimisation too - especially the new
memory barrier macro.

bob-wss wrote on Friday, October 04, 2019:

The same problem exists in V10.2.1, also the same workaround works for it.
I noticed the linker error does not occur if vTaskSuspend() is called anywhere from the project, because that calls vTaskSwitchContext().

hs2sf wrote on Friday, October 04, 2019:

I’ve solved this issue in while ago by adding:

#ifndef portDONT_DISCARD
	#define portDONT_DISCARD        __attribute__(( used ))
#endif

just below the portFORCE_INLINE macro definition in GCC/…/portmacro.h
And using it for the vTaskSwitchContext declaration.

portDONT_DISCARD void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION;

This works regardless of using or even including vTaskSuspend .
The patch in xPortStartScheduler calling vTaskSwitchContext to trick LTO never worked for me.
Just my 2ct.

rtel wrote on Friday, October 04, 2019:

I thought this was in the code already, but now I look again I see it is
only in the ARMv8-M code (Cortex-M33/M23) not in the ARMv7-M code
(Cortex-M0/3/4/7).

rtel wrote on Friday, October 04, 2019:

…forgot to say we will add into the V7-M ports too.

I’m having the same problem in Cypress:

PSoC Creator 4.3 (4.3.0.1445)

Name: ARM GCC Generic
Version: 4.3.0.1445
Company: Cypress Semiconductor
Description: ARM GNU Generic

with FreeRTOS 10.0.1 from Peripheral Driver Library 3.1.1.

I can work around it by changing line 2224 in task.h:

void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION;

to

void vTaskSwitchContext( void ) __attribute__(( used )) PRIVILEGED_FUNCTION;

Should I raise this in the Partners & Sponsors > Cypress category?

Can’t put compiler specific syntax in a file that is built by more than 20 different compilers, but there are other ways - which kernel port are you using? For example, is this an ARM Cortex-M4F, or something else?

@Richard You can :sunglasses:
By adding portDONT_DISCARD to portmacros.h and tag vTaskSwitchContext with it as proposed here.
Would be nice if I could ditch this little patch I’m keep adding for every update :slight_smile:

1 Like

Is this what you’re asking?

$ arm-none-eabi-gcc.exe --version
arm-none-eabi-gcc.exe (GNU Tools for ARM Embedded Processors 6-2017-q2-update) 6.3.1 20170620 (release) [ARM/embedded-6-branch revision 249437]
Copyright (C) 2016 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.

I dunno why Cypress uses such an old version.

Even updating GCC to e.g. 9.2.1 as provided by ARM and I’m using doesn’t prevent adding the used attribute.

No, I’m asking which FreeRTOS port are you using, not which compiler are you using. FreeRTOS runs on more than 40 different architectures, and some 20+ compilers - each combination of a compiler and an architecture is considered a port. I know which compiler you are using, but I don’t know which port you are using. Does the chip you use have an ARM Cortex-M4 core? If not, which core does it have? ARM9, ARM7, Cortex-M7, Cortex-M33, etc. are all examples of ARM cores, which I’m assuming this is. Alternatively, if you are not sure, tell me the part number of the chip you are using.

I see your point Richard. I can only say that the GCC ARM Cortex-M3 and M4 ports are affected. That’s what I’m using for my work. Seems that up to now GCC LTO is not smart enough to handle/detect function calls from inline assembler code properly. So I guess all GCC ports with a similar implementation like CM3/4 where asm xPortPendSVHandler calls vTaskSwitchContext might cause troubles using LTO. Unfortunately.
In rare cases it might cause other, hard to find and to fix troubles, too.
But the benefit in terms of code size/speed is pretty good and I found a solution for the 1-2 other issues I’ve encountered.

Yes, it has a 32-bit ARM Cortex-M4 core.

https://www.cypress.com/documentation/datasheets/psoc-6-mcu-psoc-63-ble-datasheet-programmable-system-chip-psoc

In which case I would expect this line: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/master/portable/GCC/ARM_CM4F/port.c#L387 (which cannot be reached, but the compiler doesn’t know that) to prevent vTaskSwitchContext() from being removed. Can you post the map file generated by the compiler.

I’ve verified my patch. While migrating to GCC6.3.1 I’ve noticed that the added call to vTaskSwitchContext in Source\portable\GCC\ARM_CM3\port.c::xPortStartScheduler didn’t help (anymore) with my relevant optimizer flags -Os -flto. I’m pretty sure the optimizer just inlines this call.
Please note that I’ve configured INCLUDE_vTaskSuspend 0 and also the other optional config’s used in vTaskSwitchContext are disabled making it a very small function.
It’s getting harder and harder to trick the optimizers these days…

My thinking is this needs to be entered as a bug in the GCC tracker. By definition, optimization must not break valid code, and calling a C function from in line assembly is valid. If LTO doesn’t take into account possible references to the code in question from in line assembly, or other sources that don’t give it the details of the call, it is broken.

Please have a look at this commit, which will fix the issue (thanks Gaurav) https://github.com/FreeRTOS/FreeRTOS-Kernel/commit/07e672c448e2a4ea56ae793f1c6dae26d908b16e

It defines the portDONT_DISCARD macro for the V7-M ports. It was already defined for the V8-M ports.

1 Like

Richard, I see that line in my copy. Do you still want the map file? With or without the __attribute__((used)) fix?

Hi Carl,

Does the above mentioned commit address your problem or are you still getting linker error: https://github.com/FreeRTOS/FreeRTOS-Kernel/commit/07e672c448e2a4ea56ae793f1c6dae26d908b16e

Thanks.