This post is older than 2 years and might not be relevant anymore
More Info: Consider searching for newer posts

Copying from ble_gatts_evt_write_t data member causes hard fault

I have a question on - to me - seemingly weird behavior about copying data out of the event from the soft device.

Situation: I have a 32 bit integer characteristic which is stored in the soft device and I am processing a notification and got a ble_gatts_evt_write_t structure. Now, when I try to copy the 32 bit integer from evt_write->data I can think of two ways of C doing that:

(1) Cast the uint8_t pointer into a long pointer and then just copy the value by referencing my pointer.

long timestamp = *((long*)(evt_write->data));
// Disassembly:
adds r4, #18
ldr r4, [r4]

But this causes a hard fault! Why?

(2) Using the standard library function memcpy to copy the value into my stack variable.

This works without problems:

 long timestamp = -1;
 // Disassembly
 movs r3, #1
 rsbs r3, r3 ,#0
 str r3, [sp, #4]

 memcpy(&timestamp, evt_write->data, sizeof(long));
 // Disassembly
 adds r1, r4, #0
 adds r1, #18
 add r0, sp, #4
 movs r2, #4
 bl 0x000284C0 <__aeabi_memcpy4>

My question here is - why does (2) work and (1) does not? I don't fully understand the disassembly and can only acknowledge that variant 1 obviously uses the LDR instruction whereas the other variant mainly jumps to memcpy and then some magic happens.

The compiler used here is GCC 4.9.

  • This is an alignment problem. The ARM CPU can only access a 32 bit integer in memory when it's stored on a 32 bit boundary (the memory address modulo 4 equals 0). When the memory address isn't on a 4-byte boundary the hard fault occurs.

    When you declare a 32-bit stack variable, the C compiler knows that it has to place it on a 4-byte boundary so it can be accessed. But when you try to access the value within the event structure through the uint8_t *, it's at an arbitrary memory location that probably isn't on a 4-byte boundary.

  • Of course! I was mostly confused because this bug only occurred when I switched from the SDK 11 alpha to the final one, but apparently the alignment previously existed as a mere coincidence and masked the wrong code.

Related