10
votes

I work with GCC-ARM-Embedded and FreeRTOS. FreeRTOS has the function vTaskSwitchContext() which is used only in some inline assembler code.

The problem is: When I use LTO, GCC does not consider the inline assembler code and thinks the function is not used, thus removes it. The linker then fails because the function call in the inline assembler code cannot be resolved.

I would apply __attribute__((used)) but I don't want to touch the FreeRTOS code (it's generated by STM32CubeMX).

I tried putting this in my code, but actually GCC is smart enough to not allow this to work:

if(false)
    vTaskSwitchContext();

Is there some way to tell GCC in a different source file, or via parameter, that this function should not be removed?

Example

// file1.c
void vTaskSwitchContext( void )
{
    ...
}

// file2.c
void xPortPendSVHandler( void )
{
    __asm volatile
    (
    ...
    "   isb                                 \n"
    "   bl vTaskSwitchContext               \n"
    "   mov r0, #0                          \n"
    ...
    );
}
4
I wonder how this can happen. The linker sees object files and their external references. It should not matter whether a symbol is referenced from C code or from inline assembly.undur_gongor
@undur_gongor: LTO changes many things and can cause surprises, in general. The "linker" does not actually see object files and external references, rather, the linker acts as a front-end to the compiler back-end, and then links the results from the compiler back-end.Dietrich Epp

4 Answers

11
votes

Try calling the function from a separate function which is marked used.

void dummyFunction(void) __attribute__((used));

// Never called.
void dummyFunction(void) {
    vTaskSwitchContext();
}
6
votes

You can add -Wl,--undefined=vTaskSwitchContext to your LDFLAGS.

0
votes

For some reason, the solution that Dietrich proposed didn't work for me. I'm using Infineon's DAVE 4 (basically eclipse with a fancy code generation plugin for their line of XMC microcontrollers), which may be the reason why it didn't work. For me, I had to call vTaskSwitchContext() after vTaskStartScheduler():

int main(){

    initializationCode();

    vTaskStartScheduler();

    //Code never reaches here
    vTaskSwitchContext();
}
0
votes

If your version of FreeRTOS uses already uses the macro portDONT_DISCARD for vTaskSwitchContext() you can define portDONT_DISCARD in your own portmacro.h

#define portDONT_DISCARD __attribute__((used))

Essentially backporting https://github.com/FreeRTOS/FreeRTOS-Kernel/commit/07e672c448e2a4ea56ae793f1c6dae26d908b16e