Home > Mobile >  ARM GCC produces unaligned STRD
ARM GCC produces unaligned STRD

Time:10-19

I am using GCC to compile a program for an ARM Cortex M3.
My program results in a hardfault, and I am trying to troubleshoot it.

GCC version is 10.3.1 but I have confirmed this with older versions too (i.e. 9.2).

The hardfault occurs only when optimizations are enabled (-O3).

The problematic function is the following:

void XTEA_decrypt(XTEA_t * xtea, uint32_t data[2])
{
    uint32_t d0 = data[0];
    uint32_t d1 = data[1];
    uint32_t sum = XTEA_DELTA * XTEA_NUMBER_OF_ROUNDS;

    for (int i = XTEA_NUMBER_OF_ROUNDS; i != 0; i--)
    {
        d1 -= (((d0 << 4) ^ (d0 >> 5))   d0) ^ (sum   xtea->key[(sum >> 11) & 3]);
        sum -= XTEA_DELTA;
        d0 -= (((d1 << 4) ^ (d1 >> 5))   d1) ^ (sum   xtea->key[sum & 3]);
    }

    data[0] = d0;
    data[1] = d1;
}

I noticed that the fault happens in line:

    data[0] = d0;

Disassembling this, gives me:

49          data[0] = d0;
0000f696:   lsrs    r0, r3, #5
0000f698:   eor.w   r0, r0, r3, lsl #4
0000f69c:   add     r0, r3
0000f69e:   ldr.w   r12, [sp, #4]
0000f6a2:   eors    r5, r0
0000f6a4:   subs    r2, r2, r5
0000f6a6:   strd    r2, r3, [r12]
0000f6aa:   add     sp, #12
0000f6ac:   ldmia.w sp!, {r4, r5, r6, r7, r8, r9, r10, r11, pc}
0000f6b0:   ldr     r3, [sp, #576]  ; 0x240
0000f6b2:   b.n     0xfda4 <parseNode 232>

And the offending line is specifically:

0000f6a6:   strd    r2, r3, [r12]

GCC generates code that uses an unaligned memory address with strd, which is not allowed in my architecture.

How can this issue be fixed?
Is this a compiler bug, or the code somehow confuses GCC?
Is there any flag to alter this behavior in GCC?

The aforementioned function belongs to an external library, so I cannot modify it.
However, I prefer a solution that makes GCC produce the correct instructions, instead of modifying the code, as I need to ensure that this bug will actually be fixed, and it is not lurking elsewhere in the code.


UPDATE
Following the recommendations in the comments, I was suspecting that the function itself is called with unaligned data.

I checked the whole stack frame, all previous function calls, and my code does not contain casts, unaligned indexes in buffers etc, in contrast to what I had in mind initially.

The problem is that the buffer itself is unaligned, as it is defined as:

typedef struct {
    uint32_t var1;
    uint32_t var2;
    uint8_t var3;
    uint8_t buffer[BUFFER_SIZE];
    uint16_t var4;
    // More variables here...
} HDLC_t;

(And later cast to uint32_t by the external library).

Swapping places between var3 and buffer solves the issue.

The thing is that again this struct is defined in a library that is not in my control.

So, can GCC detect this issue between the libraries, and either align the data, or warn me of the issue?

CodePudding user response:

So, can GCC detect this issue between the libraries, and either align the data, or warn me of the issue?

Yes it can, it does and it must do in order to be C compliant. This is what happens if you run gcc at default settings and attempt to pass a uint8_t pointer (HDLC_t buffer member) to a function expecting a uint32_t [2]:

warning: passing argument 2 of 'XTEA_decrypt' from incompatible pointer type [-Wincompatible-pointer-types]

This is a constraint violation, meaning that the code is invalid C and the compiler already told you as much. See What must a C compiler do when it finds an error? You could turn on -pedantic-errors if you wish to block gcc C from generating a binary executable out of invalid C code.

As for how to fix the code if you are stuck with that struct: memcpy the buffer member into a temporary uint32_t [2] array and then pass that one to the function.

You could also declare the struct member as _Alignas(uint32_t) uint8_t buffer[100]; but if you can modify the struct you might as well re-arrange it instead, since _Alignas will insert 3 wasteful padding bytes.

CodePudding user response:

The easiest way is to align data to 8bytes.
You should declare the array like:

__attribute__((aligned(8))) uint32_t data[2];
  • Related