2

Target: ARM Cortex-A9
GCC Version: 4.9.2

Hello everyone,

I have a problem with GCC producing core that causes data aborts because of unaligned accesses. I isolated a piece of code that shows the problem. I don't know how to make GCC to handle this correctly. Help would be appreciated!

struct B
{
    char c1;
    char c2;
    char c3;
    char c4;
};

struct A
{
    char c;
    struct B b;
    char c2;
};

int main(int argc, char** argv)
{
    struct A a;
    a.c2 = 42;    // Works fine
    a.b.c1 = 42   // Works fine, too
    struct B b;
    b = a.b;      // Crashes because of unaligned access
    return 0;
}

The memory layout looks like this:

a           struct A    48  S:0x3FFFFFE0    R/W
  c         char        8   S:0x3FFFFFE0    R/W
  b         struct B    32  S:0x3FFFFFE1    R/W
    c1      char        8   S:0x3FFFFFE1    R/W
    c2      char        8   S:0x3FFFFFE2    R/W
    c3      char        8   S:0x3FFFFFE3    R/W
    c4      char        8   S:0x3FFFFFE4    R/W
  c2        char        8   S:0x3FFFFFE5    R/W

Now for the first two assignments commands like

a.c2 = 42;     : STRB     r3,[r11,#-7]       with r11 = 0x3FFFFFEC

are produced and they work fine.

However, for the last assignment,

b = a.b;       : LDR      r0,[r2,#0]         with r2 = 0x3FFFFFE1

is produced what results in a data abort because of unaligned access. GCC issues no warnings or anything about that.

Does anyone know how to fix that?

Btw., alignment attributes in all struct declarations is not an option in my project.

imreal
  • 10,178
  • 2
  • 32
  • 48
matrns
  • 31
  • 2
  • 6
  • Seems to work http://ideone.com/vr3Szy – imreal Jan 22 '16 at 16:34
  • 3
    What particular build/configuration of GCC are you using, with what invocation options, and under what circumstances have you got the SCTLR.A bit set, or are accessing something other than normal memory, to cause an unaligned LDR to fault at all (given that it's a v7 core)? – Notlikethat Jan 22 '16 at 16:34
  • The program itself is perfectly valid C++, so there shouldn't be any warnings. – eerorika Jan 22 '16 at 16:39
  • Probably because the B has a size of 32 so it is attempted to be loaded from memory at once but it is misaligned: b struct B 32 S:0x3FFFFFE1 R/W. Swapping of members b and c of A will probably work. I am not sure, I never used GCC for ARM processor. – Marian Spanik Jan 22 '16 at 16:40
  • Have you by any chance enabled `#pragma pack` before? Is this the whole code you are running? – fuz Jan 22 '16 at 16:49
  • A quick test on godbolt's GCC shows this particular line as `ldr r0, [r2] @ unaligned`, which implies that the compiler perfectly knows that it is unaligned access, but doesn't feel guilty at all about it. – ElderBug Jan 22 '16 at 17:22
  • Do you get different results if you use the `-mno-unaligned-access` flag when compiling the program ? – nos Jan 22 '16 at 17:32
  • Unaligned accesses are not forbidden on Cortex A9 in all situations. Like @nos commented above, you need to explicitly tell the compiler to not allow unaligned accesses when compiling code. – Nominal Animal Jan 22 '16 at 17:41
  • 1
    I see this as a bug in GCC. A work around is to force `B` to word-aligned. – user3528438 Jan 22 '16 at 18:50
  • Somewhat related: http://stackoverflow.com/q/8568432/827263 -- but you're not specifying packing, so you *shouldn't* be having this problem. The compiler should either align the `b` member properly, or it should generate code (*by default*) to access it properly. Looks like a bug in gcc. What happens if you replace `struct B` by `int`? My guess is that gcc assumes `struct B` doesn't need to be aligned (since it's a struct containing only `char` members), but then generates code that accesses it as a single 32-bit quantity. – Keith Thompson Jan 22 '16 at 18:53
  • 1
    @Keith It's not a bug, GCC will just optimise a 4-byte `memcpy` into a single pair of 32-bit load/store instructions when it believes it's targeting a v7 core operating on normal memory such that alignment doesn't matter (just as it would on x86). The real question is what aspect of the OP's system caused that assumption to be false. – Notlikethat Jan 26 '16 at 01:03
  • @Notlikethat: Good question. I am targeting a normal area of SDRAM, no device memory or anything else. However, it's a bare metal program and there is no MMU or anything between. – matrns Jan 27 '16 at 09:32
  • Ah, right - with the MMU off all data accesses are treated as if to Strongly-Ordered memory, not Normal, hence _do_ require correct alignment. I don't think there's any better way of communicating that to GCC than the accepted answer. – Notlikethat Jan 27 '16 at 18:52

1 Answers1

3

If you use -mno-unaligned-access you get the correct result.

It was a surprise to me that it didn't do this by default as it's so oftenly wrong to do unaligned memory access on arm.

Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
skyking
  • 13,817
  • 1
  • 35
  • 57
  • There's no need to use link shorteners, because there's no limit on the length of a link in an answer. – Dietrich Epp Jan 22 '16 at 18:53
  • 4
    It shouldn't be too much of a surprise - consider the Linaro arm-linux-gnueabihf build of GCC powering the linked example: that's targeted for Linux, and configured to emit code for v7 CPUS by default, therefore it knows that _for those circumstances_ emitting unaligned LDR/STR is perfectly fine, thus will happily do so in the absence of being told otherwise. Sure, problems would arise if you then took that code and ran it on a pre-v6 CPU (with the old unaligned access model) or different target ABI (e.g. no-MMU bare-metal), but that's hardly the compiler's fault... – Notlikethat Jan 22 '16 at 19:31
  • Thank you, that helped! :-) – matrns Jan 26 '16 at 10:11