Search code examples
memory-alignmentcortex-mvariadic-functionsgcc4.9unsigned-long-long-int

va_arg on ARM Cortex-M4, uint64_t and GCC 4.9


I have the following piece of code:

__attribute__((aligned(0x1000))) static void doVariadic(const uint32_t fmt, ...);

__attribute__((aligned(0x1000))) va_list ap;
__attribute__((aligned(0x1000))) uint32_t value1 = 0xABABABAB;
__attribute__((aligned(0x1000))) uint64_t value2 = 0xF0F0F0F0E1E1E1E1LLU;
__attribute__((aligned(0x1000))) uint32_t value3 = 0x24242424;
__attribute__((aligned(0x1000))) uint32_t arg1;
__attribute__((aligned(0x1000))) uint64_t arg2;
__attribute__((aligned(0x1000))) uint32_t arg3;


__attribute__((aligned(0x1000))) int main(void)
{
    doVariadic(0x0UL, value1, value2, value3);
}


__attribute__((aligned(0x1000))) static void doVariadic(const uint32_t fmt, ...)
{
    va_start(ap, fmt);

    arg1 = va_arg(ap, uint32_t);
    arg2 = va_arg(ap, uint64_t);
    arg3 = va_arg(ap, uint32_t);

    UNUSED(arg1);
    UNUSED(arg2);
    UNUSED(arg3);
}

When this is executed, I get the following values:

arg1 = 0xABABABAB

arg2 = 0x24242424F0F0F0F0

arg3 = 0x010048E7

Variable arg3 seems to contain an address in Flash.

I've read somewhere that this could be due to stack alignment (this is why you see all these alignment attribute and especially why I've externalized my variables outside of the function scope). I've also made sure my code is compiled using the "-mabi=aapcs -std=c99" flags.

I've looked at the {r0-r3} registers when the function is called and they seem to properly contained the first 3 arguments correctly (r2 and r3 containing the 64-bit value in little-endian words).

I've noticed that during an exception (Hard Fault for exemple), the variadic functions properly. And since the MCU is set to use a 8-bit stack when entering an exception routine, it makes sense this would be the explanation to what's happening.

Any help on understanding what's really happening would be really appreciated? How could this be fixed?


Solution

  • I've managed to find my own answer after reading the following thread: http://comments.gmane.org/gmane.comp.hardware.microcontrollers.ethernut/14053

    I had misunderstood that it wasn't the stack pointer who needs to be aligned but apparently the top of my stack entirely, which wasn't 8-byte aligned. To achieve this I've had to modify my linker file from:

    _init_stack = (ORIGIN(ram) + LENGTH(ram) - 4);
    

    To:

    _init_stack = ((ORIGIN(ram) + LENGTH(ram) - 4) & 0xFFFFFFF8);