Search code examples
gccarmcortex-m

ARM GCC 10.3 & 13.x - invalid parameter for a function pointer with -Os


In Cortex-M7, I have a function, compiled with -Os, that is in the specific address, and I call it through the function pointer, like so (+1 is for thumb mode):

    //Some code above...
    memset(.....)

    uint32_t (*caller)(uint32_t taskid, ...);
    caller = (uint32_t(*)(uint32_t, ...))((uint32_t)0x08020000 + 1U);
    for (size_t i = 0; i < 3; ++i) {
        caller(i);
    }

Function gets called 3 times and returns, but i parameter inside the function is not 0,1,2, but rather garbage,0,1. This is not the case if I do not use -Os compiler option.

Inspecting disassembly, it seems that for loop is unrolled, with movs r0, #1 and movs r0, #2, and it was even re-ordered.

    memset(shared_ram, 0x00, sizeof(*shared_ram));
 8000968:   2000        movs    r0, #0
        caller(i);
 800096a:   4d1a        ldr r5, [pc, #104]  ; (80009d4 <main+0xc0>)
    memset(shared_ram, 0x00, sizeof(*shared_ram));
 800096c:   6018        str r0, [r3, #0]
 800096e:   6058        str r0, [r3, #4]
 8000970:   6098        str r0, [r3, #8]
        caller(i);
 8000972:   47a8        blx r5
 8000974:   2001        movs    r0, #1
 8000976:   47a8        blx r5
 8000978:   2002        movs    r0, #2
 800097a:   47a8        blx r5

Is this a compiler bug?

Cortex-M7 cache is not enabled, if that matters.

Compilation flags:

-fdata-sections -ffunction-sections --specs=nano.specs -Wl,--gc-sections  -g -mthumb -mcpu=cortex-m7 -mfpu=fpv5-d16 -mfloat-abi=hard -Og -g3 -ggdb -fno-eliminate-unused-debug-symbols -fdata-sections -ffunction-sections -Wall -Wextra -Wpedantic -Wno-unused-parameter -Os -std=gnu11

Solution

  • Problem is not linked to compiler (99.9% the case). My function included identical branches, therefore compiler chose default way and didn't bother with variable value.

    __attribute__((section(".security_secure_gate")))
    uint32_t se_secure_gate(uint32_t id, ...) {
        switch (id) {
            case 0: {
                __NOP();
                break;
            }
            default: {
                __NOP();
                break;
            }
        }
        return 0;
    }