I used keil mdk v5.1 for arm to develop my project. I encounterd a serious problem about LDRD instruction. Look at c codes below:
#include <stdint.h> char b[2]; char c[4]; char d[2]; typedef struct { uint32_t m_front; /* ¶ÓÍ· */ uint32_t m_rear; /* ¶Óβ */ uint16_t m_maxData; /* ¶ÓÁÐÖÐÔÊÐí´æ´¢µÄÊý¾Ý¸öÊý */ uint8_t (* ReadEmpty)(); /* ¶Á¿Õ´¦Àíº¯Êý */ uint8_t (* WriteFull)(); /* дÂú´¦Àíº¯Êý */ } DataQueue; void SystemInit() { } void main() { DataQueue* p = (DataQueue*)c; char* q = c; char* xx = b; char* yy = d; if (p->m_front == p->m_rear) { int i = 0; } }
disassembly of part of c codes is below
28: if (p->m_front == p->m_rear) 29: { 0x08000194 E9D04500 LDRD r4,r5,[r0,#0] 0x08000198 42AC CMP r4,r5 0x0800019A D101 BNE 0x080001A0 30: int i = 0; 0x0800019C 2400 MOVS r4,#0x00 31: }
I started a debug session. When I stepped over sentance "if (p->m_front == p->m_rear)", I found that program crashed(Hard fault error).
i thought that LDRD need value of r0 to a multiple of 4, but r0 value was 0x20000002 at this time.
so i have to add __align(4) keyword to make sure that address of "c" is multiple of 4 and everything is fine. you can see the codes below
#include <stdint.h> char b[2]; __align(4) char c[4]; char d[2]; typedef struct { uint32_t m_front; /* ¶ÓÍ· */ uint32_t m_rear; /* ¶Óβ */ uint16_t m_maxData; /* ¶ÓÁÐÖÐÔÊÐí´æ´¢µÄÊý¾Ý¸öÊý */ uint8_t (* ReadEmpty)(); /* ¶Á¿Õ´¦Àíº¯Êý */ uint8_t (* WriteFull)(); /* дÂú´¦Àíº¯Êý */ } DataQueue; void SystemInit() { } void main() { DataQueue* p = (DataQueue*)c; char* q = c; char* xx = b; char* yy = d; if (p->m_front == p->m_rear) { int i = 0; } }
i was confused if this was an bug of compiler? Please help me
Dear Pier
Thanks for your reply. I saw that char array is not big enough. But even it was enlarged, such issue still existed.
My MCU was STM32F103C8 which core was cortex-m3. Assume that char array's first element's address is 0x20000000. I tested severval unaligned read below.
#include <stdint.h> char array[200]; //first element's address is 0x20000000 typedef struct { uint32_t m_front; /* ¶ÓÍ· */ uint32_t m_rear; /* ¶Óβ */ uint16_t m_maxData; /* ¶ÓÁÐÖÐÔÊÐí´æ´¢µÄÊý¾Ý¸öÊý */ uint8_t (* ReadEmpty)(); /* ¶Á¿Õ´¦Àíº¯Êý */ uint8_t (* WriteFull)(); /* дÂú´¦Àíº¯Êý */ } DataQueue; void SystemInit() { } void main() { uint32_t arrayAddr = (uint32_t)array; uint32_t* pINT = (uint32_t*)(arrayAddr + 1); //address is 0x20000001 uint32_t valINT = *pINT; //It's OK. It used LRD instruction. float* pFLOAT = (float*)(arrayAddr + 1); //address is 0x20000001 float valFLOAT = *pFLOAT; //It's OK DataQueue* pQueue = (DataQueue*)(arrayAddr + 1);//address is 0x20000001 uint32_t front = pQueue->m_front; //It's OK. It used LRD instruction. uint32_t rear = pQueue->m_rear; //It's OK. It used LRD instruction. if (pQueue->m_front == pQueue->m_rear) { front = rear; //crashed here } }
I found that only LDRD instruction would crash program, but LRD instruction is OK. If i used IAR compiler to compile this program, it's OK. Because there is no LDRD instruction.
There were many casting structure(packed or unpacked) such as DataQueue in my project. what should i do to prevent those crashes?
Looking forward to your reply.
Get rid of all the "packed" attributes. They cause a whole lot of grief (including the unaligned accesses you're so worried about), for hardly any benefit.
If i used IAR compiler to compile this program, it's OK.
No, it's not OK in any sensible meaning of the word.
That program, as written, is terminally sick. The fact it doesn't die quite as horribly and immediately if built by a different compiler, or with different flags, doesn't make the program itself any healthier. It just means its manifest illness will express itself in a different way. Or do you really believe getting garbage results instead of an immediate hard fault is in any sense of the word "OK"?
Every single line of that program which you commented as "It's OK" actually causes undefined behaviour. In case you don't recognize that term: that's the worst kind of error a C program can have while still being compilable at all.
Your fixation on the instruction being used by the compiler is misleading you. It's not the compiler, nor the compiled code that's the problem here. It's the source code that's grossly incorrect.