This is the mail archive of the
gcc@gcc.gnu.org
mailing list for the GCC project.
Strict aliasing affects glibc 2.1.1 as well as Linux
- To: libc-alpha at sourceware dot cygnus dot com
- Subject: Strict aliasing affects glibc 2.1.1 as well as Linux
- From: "Joseph S. Myers" <jsm28 at cam dot ac dot uk>
- Date: Mon, 19 Jul 1999 21:47:04 +0100 (BST)
- cc: gcc at gcc dot gnu dot org
In addition to the Linux kernel, some of glibc 2.1.1's optimised
string functions have problems with type-based aliasing. The
following program, when compiled on i686-pc-linux-gnu with glibc 2.1.1
and gcc 2.95 19990716 with -O2 gives incorrect results; with -O2
-fno-strict-aliasing it gives correct results. memset should always
work as if it is assigning individual characters; I believe gcc is
implementing the ISO C aliasing rules correctly, and it is glibc's
macros that are broken.
glibc should probably use one of the union-based solutions proposed
for the Linux kernel.
The key preprocessed part of the source is the memset expansion,
(reformatted for readability), which ultimately tries to write the
output (part of an unsigned int) through an __uint16_t *.
(See below this memset expansion for the full program demonstrating the
problem.)
(__extension__ (__builtin_constant_p ( 2 ) && ( 2 ) <= 16
?
(( 2 ) == 1
? ({ void *__s = ( &f ); *((__uint8_t *) __s) = (__uint8_t) 1 ; __s; })
: ({
void *__s = ( &f );
__uint32_t *__ts = (__uint32_t *) __s;
__uint8_t __c = (__uint8_t) ( 1 );
switch ( 2 ) {
case 15: *__ts++ = __c * 0x01010101;
case 11: *__ts++ = __c * 0x01010101;
case 7: *__ts++ = __c * 0x01010101;
case 3: *((__uint16_t *) __ts)++ = __c * 0x0101;
*((__uint8_t *) __ts) = __c; break;
case 14: *__ts++ = __c * 0x01010101;
case 10: *__ts++ = __c * 0x01010101;
case 6: *__ts++ = __c * 0x01010101;
case 2: *((__uint16_t *) __ts) = __c * 0x0101; break;
case 13: *__ts++ = __c * 0x01010101;
case 9: *__ts++ = __c * 0x01010101;
case 5: *__ts++ = __c * 0x01010101;
case 1: *((__uint8_t *) __ts) = __c; break;
case 16: *__ts++ = __c * 0x01010101;
case 12: *__ts++ = __c * 0x01010101;
case 8: *__ts++ = __c * 0x01010101;
case 4: *__ts = __c * 0x01010101;
case 0: break;
}
__s;
})
)
:
(__builtin_constant_p ( 1 ) && ( 1 ) == '\0'
? ({ void *__s = ( &f ); __builtin_memset ( __s , '\0', 2 ) ; __s;})
: memset ( &f , 1 , 2 ))
)) ;
===== begin program =====
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
unsigned int f = 65536;
int
foo(void)
{
unsigned int g;
g = f;
printf("%u\n", g);
memset(&f, 1, 2);
printf("%u\n", f);
return f;
}
int
main(void)
{
foo();
exit(0);
}
===== end program =====
--
Joseph S. Myers
jsm28@cam.ac.uk