Re: Library bug or my fault?
- From: Chris Torek <nospam@xxxxxxxxx>
- Date: 23 Jul 2008 09:23:25 GMT
There has been quite a lot of noise in this thread, but *** Winter
has, I believe, identified the actual problem:
[I am not sure why the quote marks do not match up as I think the
authors here are correct]
On 22 Jul, 10:03, Steven Woody <narkewo...@xxxxxxxxx> wrote:
struct Foo {
uint8_t x;
uint8_t y;
uint8_t z;
uint8_t m[3];
};
In article <K4Epyq.HCG@xxxxxx> *** T. Winter <***.Winter@xxxxxx> wrote:
A compiler can align 'm' any way it wants to, so it may start at any byte
address. And it can require stricter alignment of the struct itself.
Right.
struct Bar
{
uint8_t m[3];
};
A compiler is allowed to require stricter alignment rules for this struct.
Likewise ... and in this case, it does.
Now we hit the interesting bits:
struct Bar bar;
struct Bar *p2 = (struct Bar*)(foo->m);
This is a cast that does not necessarily work as you want because
of alignment differences.
The cast happens to work on the ARM, producing a pointer that is
"one byte away" from correct alignment. This would not (and does
not) matter to the compiler in some cases, but...
The next bit of code (which has been snipped here) does a memcpy(),
in effect doing:
memcpy(&bar, p2, 3);
The compiler is allowed to assume that p2 (which can only correctly
point to an actual instance of a "struct Bar") is 4-byte aligned,
and hence emit a "fast and inline" memcpy that does:
load two bytes from address given by p2
store two bytes to first two bytes of "bar"
load third byte from address given by p2+2
store third byte to third byte of "bar"
Here is where things get interesting. On the ARM, if you supply an
unaligned address to a load or store instruction that is loading or
storing more than one byte, the machine IGNORES THE LOW ORDER BITS.
In effect, this "means":
*(short *)&bar = *(short *)((uintptr_t)p2 & ~1);
*((char *)&bar + 2) = *((char *)p2 + 2);
Since p2 has the low order bit set, the hardware's "& ~1" (clear
low bit) has the effect of subtracting 1. So bar.m[0] will actually
take on the value of foo->z and bar.m[1] will take on the value of
foo->m[0], while bar.m[2] will take on the value of foo->m[2].
The lesson here is the same as always: "If you lie to the compiler,
it will get its revenge."
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: gmail (figure it out) http://web.torek.net/torek/index.html
.
- References:
- Re: Library bug or my fault?
- From: Nick Keighley
- Re: Library bug or my fault?
- From: Steven Woody
- Re: Library bug or my fault?
- From: Nick Keighley
- Re: Library bug or my fault?
- From: *** T. Winter
- Re: Library bug or my fault?
- Prev by Date: Re: Shifting in C
- Next by Date: Re: A very interesting book
- Previous by thread: Re: Library bug or my fault?
- Next by thread: Re: Library bug or my fault?
- Index(es):