ledow
Member
I've just discovered (well, uncovered might be a more accurate term) a little bug in the memset function for the version of libc shipped with the Open2x toolchain. I have to say that I just "hit" this bug, happened to notice that it was a problem with memset and somebody else did all the clever tracing back to find its cause. Thanks to Simon Tatham (of PuTTY fame) for that. I don't know if it's a particular bug in this version of libc, or if it the libc was compiled wrong, or if it's a bug with the ARM-patches to this libc. However, somebody else may hit it so it's in everyone's interest that I share the problem.
I'll let a quote from Simon's email explain the problem, which occurs if you try to do:
CODE
memset(a pointer, a negative value < -1, a size).
QUOTE
In fact, the problem turned out to be in memset itself, which was in libc.so.6 in the arm-open2x-linux-apps-gcc-4.1.1-glibc-2.3.6_i686_linux.tar.bz2.zip archive you linked....
I think this is in fact a failure of standards compliance in that libc's memset. The C standard says that memset(s,c,n) fills the
memory with the value of c _converted_ to an unsigned char. That is, memset must behave as if it clips the value to fit in an unsigned char before doing anything. The memset in that libc does not.
What's actually happening is that memset is taking the input c value and doing the equivalent of
c |= c << 8;
c |= c << 16;
to get a 32-bit word containing four copies of c. Then it fills memory with that, a word at a time, and cleans up a few bytes at
each end separately. But, of course, if c has the top 24 bits set on input, then those bitwise OR operations don't make any difference, and memory will be filled with a 32-bit word in which three of the bytes are 0xFF no matter what the other one was. So memset ought to be doing
c &= 0xFF;
before it starts; and it isn't.
He suggests a workaround of replacing:
CODE
memset(thing, -2, stuff);
with
CODE
memset(thing, (unsigned char)-2, stuff);
Or you can just replace the memset with an explicit loop that sets each element of your array to -2, for example. Note that most libc versions don't require the explicit cast at all and just function normally (so your PC won't hit this bug even if it compiles the same code). It's only when you link that same code using that version of libc in the Open2x toolchain that you get problems. Rather than:
CODE
-2 -2 -2 -2 -2
filling the "thing", you get:
CODE
-1 -2 -2 -2 -1 -2 -2 -2 -1
or similar.
Hats off to Simon for doing the majority of the legwork (including downloading the Open2X toolchain, reading through pages of ARM assembly and eventually discovering the actual cause of the problem), and for mentioning something completely different that made me find the bug in the first place.
I'll let a quote from Simon's email explain the problem, which occurs if you try to do:
CODE
memset(a pointer, a negative value < -1, a size).
QUOTE
In fact, the problem turned out to be in memset itself, which was in libc.so.6 in the arm-open2x-linux-apps-gcc-4.1.1-glibc-2.3.6_i686_linux.tar.bz2.zip archive you linked....
I think this is in fact a failure of standards compliance in that libc's memset. The C standard says that memset(s,c,n) fills the
memory with the value of c _converted_ to an unsigned char. That is, memset must behave as if it clips the value to fit in an unsigned char before doing anything. The memset in that libc does not.
What's actually happening is that memset is taking the input c value and doing the equivalent of
c |= c << 8;
c |= c << 16;
to get a 32-bit word containing four copies of c. Then it fills memory with that, a word at a time, and cleans up a few bytes at
each end separately. But, of course, if c has the top 24 bits set on input, then those bitwise OR operations don't make any difference, and memory will be filled with a 32-bit word in which three of the bytes are 0xFF no matter what the other one was. So memset ought to be doing
c &= 0xFF;
before it starts; and it isn't.
He suggests a workaround of replacing:
CODE
memset(thing, -2, stuff);
with
CODE
memset(thing, (unsigned char)-2, stuff);
Or you can just replace the memset with an explicit loop that sets each element of your array to -2, for example. Note that most libc versions don't require the explicit cast at all and just function normally (so your PC won't hit this bug even if it compiles the same code). It's only when you link that same code using that version of libc in the Open2x toolchain that you get problems. Rather than:
CODE
-2 -2 -2 -2 -2
filling the "thing", you get:
CODE
-1 -2 -2 -2 -1 -2 -2 -2 -1
or similar.
Hats off to Simon for doing the majority of the legwork (including downloading the Open2X toolchain, reading through pages of ARM assembly and eventually discovering the actual cause of the problem), and for mentioning something completely different that made me find the bug in the first place.