0

I'm getting hardfault with following code using both 9-2020-q2-update and 8-2019-q3-update arm-none-eabi toolchains:

#define ALIGNMENT ( 1 )
#pragma pack(push, ALIGNMENT)
typedef struct a {
  int16_t i16;
  uint64_t u64;
} a_t;
#pragma pack(pop)

[...]

// s is passed as void*
((a_t *)s)->u64 = (uint64_t)-1;
((a_t *)s)->i16 = 0;

With an alignment defined as macro in #pragma pack gcc generates following code with -O0:

217         ((a_t *) s)->u64 = (uint64_t)-1;
08043654:   ldr     r1, [r7, #16]
08043656:   mov.w   r2, #4294967295
0804365a:   mov.w   r3, #4294967295
0804365e:   strd    r2, r3, [r1, #8]
218         ((a_t *) s)->i16 = 0;
08043662:   ldr     r3, [r7, #16]
08043664:   movs    r2, #0
08043666:   strh    r2, [r3, #0]

r7 contains s value, which is not aligned to 4-byte boundary. strd generates hardfault.

Correct assembly is generated when i use #pragma pack(push, 1):

217         ((a_t *) s)->u64 = (uint64_t)-1;
08043918:   ldr     r3, [r7, #16]
0804391a:   adds    r3, #2
0804391c:   mov.w   r2, #4294967295
08043920:   strb    r2, [r3, #0]
08043922:   mov.w   r2, #4294967295
08043926:   strb    r2, [r3, #1]
08043928:   mov.w   r2, #4294967295
0804392c:   strb    r2, [r3, #2]
0804392e:   mov.w   r2, #4294967295
08043932:   strb    r2, [r3, #3]
08043934:   mov.w   r2, #4294967295
08043938:   strb    r2, [r3, #4]
0804393a:   mov.w   r2, #4294967295
0804393e:   strb    r2, [r3, #5]
08043940:   mov.w   r2, #4294967295
08043944:   strb    r2, [r3, #6]
08043946:   mov.w   r2, #4294967295
0804394a:   strb    r2, [r3, #7]
218         ((a_t *) s)->i16 = 0;
0804394c:   ldr     r3, [r7, #16]
0804394e:   movs    r2, #0
08043950:   strb    r2, [r3, #0]
08043952:   movs    r2, #0
08043954:   strb    r2, [r3, #1]

Is there any way to correctly pass alignment defined as macro to #pragma pack? I've got packed struct definitions scattered around the whole codebase. There are some memory constrained platforms, where i want to align them to one-byte boundary and some platforms, where it isn't so important, and performance gains would be nice. It would be convenient to have alignment defined once and used in all pragmas.

rkrahl
  • 1,159
  • 12
  • 18
  • [Reproduced the assembly on godbolt](https://godbolt.org/z/cu4WGn). Why use msvc syntax with `pragma push` and not gcc syntax `__attribute__((__aligned__`? – KamilCuk Jul 09 '20 at 11:59
  • 1
    you cant get segfault using STM32. It can be HardFault - but for sure not segfault – 0___________ Jul 09 '20 at 12:05

1 Answers1

1

Just use proper gcc extension attribute:

#define ALIGNMENT 1
typedef struct a {
    int16_t i16;
    uint64_t u64;
} a_t __attribute__((__aligned__(ALIGNMENT)));

#pragma pack(push is a syntax that gcc keeps for compatibility with msvc. Prefer not to use it with gcc.

In the newest C standard the syntax for attributes is getting standarized. With arm-none-eabi-gcc10.1 with -std=c2x you can use [[gnu::aligned(ALIGNMENT)]] attribute.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
  • 1
    I didn't mention that this code _is_ compiled with msvc too :). Thanks for the tip though! – rkrahl Jul 09 '20 at 12:05
  • That a simple and ready to use solution would be to handle msvc and gcc code with different preprocessor paths. I guess that gcc just doesn't run preprocessor on `#pragma pack(push, ALIGNMENT)` calls (which still is strange). I thnk try with `__pragma` or `_Pragma` or how it's called, if it's supported on msvc too. – KamilCuk Jul 09 '20 at 12:08
  • I'm trying solution from this answer: https://stackoverflow.com/a/45130697/726506 and it seems to be working fine. Will update in a minute. Using #pragma has nice side effect of packing many consecutive struct definitions in file at once. – rkrahl Jul 09 '20 at 12:11
  • it seems that gcc applies info from pragma pack partially. It packs structs in memory taking it under account, but generated code to acces its members is wrong. Aforementioned solution works under gcc and msvc. – rkrahl Jul 09 '20 at 13:31