This is the mail archive of the libc-alpha@sourceware.cygnus.com mailing list for the glibc project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Strict aliasing affects glibc 2.1.1 as well as Linux


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


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]