cancel
Showing results for 
Search instead for 
Did you mean: 

Why are these bugs occuring in GCC, but not mbed's online compiler?

matt2399
Associate II
Posted on January 02, 2015 at 23:52

We've recently switched toolchains from mbed's online compiler to gcc-arm, but had several issues after doing so. Our code, which works fine when compiled online, seems to have issues once we use gcc to compile.

Execution appears to end abruptly at certain points, so we stepped through the code using gdb, and noticed that HardFault_Handler() keeps getting called just before things stop working. In an attempt to avoid stack corruption and null references, we made some of our variables static, which allowed us to progress further through the code without issue.

However, making all of our variables static is clearly not the most appropriate way to go about this, since we didn't have these problems when compiling through mbed. We have verified that we don't have buffer overflows or array-bounds issues. Could it be something more nuanced with gcc that could be causing issues (e.g., stack reuse) with these variables?

Additionally, this problem seems to only happen after context switches in mbed-rtos. Has anyone else had problems with registers be restored improperly?

#gcc #toolchain #mbed
6 REPLIES 6
carl2399
Associate II
Posted on January 03, 2015 at 01:20

While I don't used mbed, I do use GCC on an almost daily basis for multiple commercial projects using STM32s. I have not experienced these problems you're talking about, except maybe for two:

1) If using static C++ classes, then the compiler would hard-fault on startup - I suspect the library code had some non-thumbARM. I wrote my own static class instantiator and have had no further problems with this problem.

/* Call CTORS of static objects. */ 
ldr r4, = __init_array_start
ldr r5, = __init_array_end
cmp r4,r5
beq ctorsDone
ctorsNextOne:
ldmdb r5!, {r2}
blx r2
cmp r4,r5
blo ctorsNextOne
ctorsDone:

2) The C library for memcpy (prior to 4.x??) was using ldmdb (and friends) to do the copy. However these instructions require the memory regions to be 32-bit aligned. I'm using GCC 4.7 / 4.8 and the ldmdb instruction is no longer used. The processor will hard-fault if an unaligned address is used. Generate a unified assembly listing, and go looking for memcpy. You'll should be able to see quite easily if the ldmdb instructions are being used. (ldmdb, ldmia, stmdb, stmia).
matt2399
Associate II
Posted on January 07, 2015 at 18:23

Thanks for your help, but I don't believe your post solved our issue. There were no hard faults occurring at startup, so I ignored your first suggestion. As per your second suggestion, memcpy does not appear to use any of the assembly instructions you listed:

(gdb) disas memcpy

Dump of assembler code for function memcpy:

   0x0800fb1a <+0>:     push    {r4, lr}

=> 0x0800fb1c <+2>:     movs    r3, #0

   0x0800fb1e <+4>:     cmp     r3, r2

   0x0800fb20 <+6>:     beq.n   0x800fb2a <memcpy+16>

   0x0800fb22 <+8>:     ldrb    r4, [r1, r3]

   0x0800fb24 <+10>:    strb    r4, [r0, r3]

   0x0800fb26 <+12>:    adds    r3, #1

   0x0800fb28 <+14>:    b.n     0x800fb1e <memcpy+4>

   0x0800fb2a <+16>:    pop     {r4, pc}

End of assembler dump.

Any further help would be appreciated.

Tuttle.Darrell
Associate II
Posted on January 07, 2015 at 22:52

Do you have optimizations turned off? I'm wondering if you may be missing a needed ''volatile'' keyword in one or more of your variable declarations.

tg22410
Associate II
Posted on January 19, 2015 at 17:08

Hi Jacobs!

I have the problem with ldmbd instructions (ldmia/stmia). Usually I used the 4.6 GCC version, but checking your post I decided to update the GCC to 4.8.3. Using this version and generating the assembly instructions, there are still ldmia's family instructions. How I can avoid completely the use of this functions?

Thank you!

carl2399
Associate II
Posted on January 20, 2015 at 10:53

While probably not the most elegant solution, I wrote my own memcpy a while ago, and have been using it ever since:

void
MemCpy(
void
*dst, 
const
void
*src, u32 cnt)
{
while
(cnt >= 4)
{
*(u32 *)dst = *(
const
u32 *)src;
dst = (u8 *)dst + 4;
src = (
const
u8 *)src + 4;
cnt -= 4;
}
while
(cnt--)
{
*(u8 *)dst = *(
const
u8 *)src;
dst = (u8 *)dst + 1;
src = (
const
u8 *)src + 1;
}
}

I've also had the following DMA code hanging around for an age, but have never used it, so it comes with no guarantee:

void
DMA_Copy(
void
*dest, 
void
*src, u32 size)
{
DMA1_Channel1->CCR = 0x000070C0; 
// Configure for mem2mem transfer
DMA1_Channel1->CPAR = (u32)src; 
// Set source and destination
DMA1_Channel1->CMAR = (u32)dest;
DMA1_Channel1->CNDTR = size; 
// Set size of transfer
DMA1_Channel1->CCR |= 0x00000001; 
// Start the DMA transfer
while
(!(DMA1->ISR & 0x0000001)); 
// Wait till the transfer ends
}

I also found the gcc library implementations of strcmp to be a bit bloated, and so wrote the following:

int
StrnCmp(
const
void
*str1, 
const
void
*str2, 
int
len)
{
const
u8 *s1 = (
const
u8 *)str1;
const
u8 *s2 = (
const
u8 *)str2;
while
(len && *s1 && *s2 && (*s1 == *s2)) { len--; s1++; s2++; }
if
(len)
return
*s1 - *s2; 
// String different
return
0; 
// Strings match
}
int
StrCmp(
const
void
*str1, 
const
void
*str2)
{
const
u8 *s1 = (
const
u8 *)str1;
const
u8 *s2 = (
const
u8 *)str2;
while
(*s1 && *s2 && (*s1 == *s2)) { s1++; s2++; }
if
(*s1 || *s2)
return
*s1 - *s2; 
// String different
return
0; 
// Strings match
}

Best of luck!
carl2399
Associate II
Posted on January 20, 2015 at 11:31

Returning to the original question:

I remember something from about 12 months ago - when doing C++ (as does mbed), I found that function-local static variables caused a problem. But only in class functions (normal non-class functions are fine). As I use so few of these, I moved them into the private area of the class definition instead (as non-static variables), and haven't had any problems since. With mbed you possibly don't have much control over this.

I had a quick look through the mbed source, and while I didn't find any of the static variables I described, I did find that they use a number of class-static variables. I don't tend to use them, so can't relate any personal experiences about them.

Just as a style thing, I see that mbed also uses ''inline'' quite a bit, which is something that I would generally leave up to the compiler to decide.

I might have a look at how easy it is for me to compile the mbed source, as they do seem to do a number of nice things.