FreeRTOS won't build on GCC if Linker Time Optimization is set (issue for STM32 ARM CM-7 - port.c:427: undefined reference to `vTaskSwitchContext)

daveharr wrote on Friday, March 10, 2017:

Hello, FreeRTOS won’t build on GCC if Link-Time Optimization (-flto) is set. (issue for STM32 ARM CM-7)
I consistently get this error in port.c:427: undefined reference to vTaskSwitchContext).

The vTaskSwitchContext() function is clearly defined in tasks.c. This problem can be fixed in tasks.h if vTaskSwitchContext is declared thus at line 2172: void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION attribute((used)); For some reason the double underscores before and after the word attribute do not appear in the post.

Can you fix this so that this is include in the next release? I realize this is GCC dependent so you’d have to make it apply only for a GCC build.

Thanks.

rtel wrote on Friday, March 10, 2017:

We can’t put GCC specific code in a non-GCC specific file. What happens
if you add this prototype to the port.c file specific to the Cortex-M
port you are using? Then there will be two prototypes for the same
function visible to the file, so it might puke.

daveharr wrote on Friday, March 10, 2017:

I tried that - i.e. putting the prototype

void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION attribute((used));

in port.c. The GCC compiler doesn’t object but it doesn’t get rid of the undefined reference to vTaskSwitchContext either. That is because the link time optimizer is optimizing away the actual vTaskSwitchContext function defined in tasks.c because it doesn’t see it referenced anywhere.
It is the tasks.c source code file that needs to have that prototype, or the equivalent of it somewhere.

xz8987f wrote on Friday, March 10, 2017:

This is what I have done in all my FreeRTOS ports:
Using this in task.h:

#ifdef __GNUC__ /* << EST: 'used' attribute need for LTO (Link Time Optimization) */
  void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION __attribute__((used));
#else
  void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION;
#endif

Using this in task.c:

#ifdef __GNUC__ /* << EST */
__attribute__((used)) /* using C++ compiler, vTaskSwitchContext() might be removed even with -O0? */
#endif
void vTaskSwitchContext( void )

I have seen that problem both with -Lto and compiling the RTOS in C++ mode.
I agree that using compiler specifc things like above is not ideal, but worked for me.
Another option would be to use some defines in the GCC portmacro.h

Erich

rtel wrote on Friday, March 10, 2017:

Is that an acceptable solution for you?

Yes, I think so, it is a GCC specific fix (?) in a GCC specific code. I
will add it into the main line too.

daveharr wrote on Friday, March 10, 2017:

Unfortunately I replied that it fixed the problem too soon. I subsequently edited my post above to indicate that inserting the prototype void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION attribute((used)); in the port.c file does NOT work. It is the tasks.h or tasks.c file that must contain it. I am in the fortunate position that I know I will only ever use GNU CC so I can put it directly into task.h in my own project. I realize that you can’t do that so you may want to find another solution.

rtel wrote on Saturday, March 11, 2017:

Ok - I had already made that change, but not checked it in, so its easy
to undo. Another solution is required.

Presumable if vTaskSwitchContext() was called by C code, rather than
just asm code, then the compiler/linker would know to keep the function.
Can you try calling the function, or at least referencing the
function, after the scheduler has been started? The compiler will not
know that in the GCC port prvPortStartFirstTask() will not return, so
you can call vTaskSwitchContext() after prvPortStartFirstTask() within
the xPortStartScheduler() function, which is itself in the
FreeRTOS/Source/portable/GCC/ARM_CMx/port.c file. You will notice that
prvTaskExitError() is already called there for a simliar (but not
identical) reason.

richard_damon wrote on Sunday, March 12, 2017:

I will add that I hope the issue has also been forwarded to the GCC team (unless this falls under a documentent Known Issue), as it sounds like a serious bug in the Link Time Optimization routine. Optimizations must not break valid code.

rtel wrote on Sunday, March 12, 2017:

I think in this case the offending function is only called from asm
code, so the compiler cannot see it, but as we are discussing link time
optimisation the LINKER should be able to see it.

hs2sf wrote on Sunday, March 12, 2017:

I’ve added this to compiler/platform specific portmacro.h:

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

and appended portFORCE_USED to vTaskSwitchContext decl. in task.h:

void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION portFORCE_USED;

This should solve the GCC specfic issue in a generic way.
(see feature req. ticket #90 LTO Support in FreeRTOS)

Just my 2ct, HS2

richard_damon wrote on Sunday, March 12, 2017:

My guess, is that LTO is looking at the call context of the function to see if it can be profitably inlined, or perhaps some optimiazations for the code are possible, and not seeing any context is omitting the function, but the ASM reference SHOULD raise a flag that there is a (possible) call that is from an unknown context, so the compiler needs to be conservative in optimization. Perhaps the ((used)) attribute is a way of signalling it, but to me it really shouldn’t be needed.

As Real Time Engineers commented, a simpler fix might just be to put a call to it in C code so it knows it will be needed.