[cfe-users] Union aliasing issue in clang 3.3 on FreeBSD
Sami Vaarala
sami.vaarala at gmail.com
Tue Dec 10 05:10:22 PST 2013
Hi,
I encountered a union aliasing issue on FreeBSD clang 3.3, see code
below. In short, assigning a union value to another when the union
contains a double and an array of 8 bytes causes the value to copy
incorrectly. This is dependent on compiler options, the behavior
doesn't happen without -m32 or when using -O0. It also doesn't happen
with an older clang.
Is this a clang issue, or is my code making invalid assumptions about
unions and casting? Based on a quick look at the generated asm code,
clang generates a floating point load/store to copy the value - could
this be the root cause? The code looks something like:
8048653: dd 44 24 20 fldl 0x20(%esp)
8048657: dd 5c 24 10 fstpl 0x10(%esp)
804865b: 8d 44 24 10 lea 0x10(%esp),%eax
804865f: 89 04 24 mov %eax,(%esp)
8048662: e8 61 ff ff ff call 80485c8 <dump>
Thanks for any help :-) I'm pasting the code below as I'm not sure
whether or not attachments are supported.
======== clang_aliasing.c
/*
* Problem with clang 3.3 on FreeBSD:
*
* $ clang -v
* FreeBSD clang version 3.3 (tags/RELEASE_33/final 183502) 20130610
* Target: x86_64-unknown-freebsd10.0
* Thread model: posix
*
* The problem manifests itself as follows (x86_64):
*
* $ clang -Os -m32 -std=c99 -fstrict-aliasing -fomit-frame-pointer
clang_aliasing.c
* $ ./a.out
* 11 22 33 44 00 00 f1 ff
* 11 22 33 44 00 00 f9 ff <==
* 11 22 33 44 00 00 f9 ff <==
* 11 22 33 44 00 00 f1 ff
*
* The value is corrupted even in the base care where 'a' is initialized
* as bytes and then copied with a structural assignment; the double part
* of the union is not accessed at all. The corruption is always that the
* 6th byte gets OR'd with 0x08. This bit is the highest bit of the IEEE
* double mantissa, so this is probably floating point related. The code
* generated uses a floating point load/store to copy the value except in
* the cases where the copy works.
*/
#include <stdio.h>
#include <stdint.h>
typedef union { double d; uint8_t c[8]; } my_union;
void dump(my_union *u) {
int i;
for (i = 0; i < 8; i++) { printf("%02x ", (int) u->c[i]); }
printf("\n");
}
#define COPY_FAILS(s,d) { int _i; for(_i = 0; _i < 8; _i++) { \
((uint8_t *) (d))[_i] = ((uint8_t *) (s))[_i]; \
} }
#define COPY_WORKS(s,d) { volatile int _i; for(_i = 0; _i < 8; _i++) { \
((uint8_t *) (d))[_i] = ((uint8_t *) (s))[_i]; \
} }
int main(int argc, char *argv[]) {
my_union a, b, c, d;
/* Initial value, the contents of bytes 6 and 7 seem to matter */
a.c[0] = 0x11; a.c[1] = 0x22; a.c[2] = 0x33; a.c[3] = 0x44;
a.c[4] = 0x00; a.c[5] = 0x00; a.c[6] = 0xf1; a.c[7] = 0xff;
dump(&a);
/* Copy with assignment (does not work) */
b = a; dump(&b);
/* Copy with a byte-by-byte macro (does not work) */
COPY_FAILS(&a, &c); dump(&c);
/* Copy with a byte-by-byte macro (volatile loop variable - works) */
COPY_WORKS(&a, &d); dump(&d);
return 0;
}
========
-Sami
More information about the cfe-users
mailing list