gcc, alias estrictos e historias de terror


En gcc-strict-aliasing-and-cast-through-a-union Pregunté si alguien había encontrado problemas con el juego de palabras de unión a través de punteros. Hasta ahora, la respuesta parece ser No.

Esta pregunta es más amplia: ¿Tienes alguna historias de terror sobre el ccg y el alias estricto?

Antecedentes: Citando La respuesta de AndreyT en c99-strict-aliasing-rules-in-c-gcc:

" Las reglas de alias estrictas tienen sus raíces en partes del estándar que estaban presentes en C y C++ desde el comienzo de los tiempos [estandarizados]. La cláusula que prohíbe el acceso a un objeto de un tipo a través de un lvalue de otro tipo está presente en C89/90 (6.3), así como en C++98 (3.10/15). ... Es solo que no todos los compiladores querían (o se atrevían) a aplicarlo o confiar en él."

Bueno, gcc ahora se atreve a hacerlo, con su -fstrict-aliasing interruptor. Y esto ha causado algunos problemas. Véase, por ejemplo, el excelente artículo http://davmac.wordpress.com/2009/10 / acerca de un error de Mysql, y la discusión igualmente excelente en http://cellperformance.beyond3d.com/articles/2006/06/understanding-strict-aliasing.html.

Algunos otros enlaces menos relevantes:

Así que, para repetir, ¿tienes tu propia historia de terror? Problemas no indicado por -Wstrict-aliasing, por supuesto, ser el preferido. Y otros compiladores de C también son bienvenidos.

Añadido el 2 de junio: El primer enlace en la respuesta de Michael Burr, que hace de hecho calificar como una historia de terror, es quizás un poco anticuado (de 2003). Hice una prueba rápida, pero el problema aparentemente ha desaparecido.

Fuente:

#include <string.h>
struct iw_event {               /* dummy! */
    int len;
};
char *iwe_stream_add_event(
    char *stream,               /* Stream of events */
    char *ends,                 /* End of stream */
    struct iw_event *iwe,       /* Payload */
    int event_len)              /* Real size of payload */
{
    /* Check if it's possible */
    if ((stream + event_len) < ends) {
            iwe->len = event_len;
            memcpy(stream, (char *) iwe, event_len);
            stream += event_len;
    }
    return stream;
}

La denuncia específica es:

Algunos usuarios se han quejado de que cuando el código [anterior] se compila sin el alias-fno-strict, el orden de escritura y memcpy se invierte (lo que significa que un len falso se copia mem en la secuencia).

Compiled code, using gcc 4.3.4 on CYGWIN wih-O3 (please correct me if I am wrong my my assembler is a bit rusty!):

_iwe_stream_add_event:
        pushl       %ebp
        movl        %esp, %ebp
        pushl       %ebx
        subl        $20, %esp
        movl        8(%ebp), %eax       # stream    --> %eax
        movl        20(%ebp), %edx      # event_len --> %edx
        leal        (%eax,%edx), %ebx   # sum       --> %ebx
        cmpl        12(%ebp), %ebx      # compare sum with ends
        jae L2
        movl        16(%ebp), %ecx      # iwe       --> %ecx
        movl        %edx, (%ecx)        # event_len --> iwe->len (!!)
        movl        %edx, 8(%esp)       # event_len --> stack
        movl        %ecx, 4(%esp)       # iwe       --> stack
        movl        %eax, (%esp)        # stream    --> stack
        call        _memcpy
        movl        %ebx, %eax          # sum       --> retval
L2:
        addl        $20, %esp
        popl        %ebx
        leave
        ret

Y para el segundo eslabón de la respuesta de Miguel,

*(unsigned short *)&a = 4;

Gcc normalmente (siempre?) dar una advertencia. Pero yo creo una solución válida para esto (para gcc) es usar:

#define CAST(type, x) (((union {typeof(x) src; type dst;}*)&(x))->dst)
// ...
CAST(unsigned short, a) = 4;

He preguntado si esto está bien en gcc-strict-aliasing-and-casting-through-a-union , pero hasta ahora nadie está en desacuerdo.

Author: Community, 2010-06-02

6 answers

No hay historia de terror propia, pero aquí hay algunas citas de Linus Torvalds (lo siento si estas ya están en una de las referencias enlazadas en la pregunta):

Http://lkml.org/lkml/2003/2/26/158 :

Fecha Mié, 26 Feb 2003 09:22:15 -0800 Asunto Re: Compilación no válida sin-fno-strict-aliasing De Jean Tourrilhes

El miércoles, 26 de febrero de 2003 a las 04:38:10PM +0100, Horst von Brand escribió:

Jean Tourrilhes dijo:

Me parece un error del compilador... Algunos usuarios se han quejado de que cuando el siguiente código es compilado sin el-fno-strict-aliasing, el orden de la escritura y memcpy se invierte (lo que significa que un len falso es mem-copiado en el flujo). Código (de linux / include / net / iw_handler.h):

static inline char *
iwe_stream_add_event(char *   stream,     /* Stream of events */
                     char *   ends,       /* End of stream */
                    struct iw_event *iwe, /* Payload */
                     int      event_len)  /* Real size of payload */
{
  /* Check if it's possible */
  if((stream + event_len) < ends) {
      iwe->len = event_len;
      memcpy(stream, (char *) iwe, event_len);
      stream += event_len;
  }
  return stream;
}

En mi humilde opinión, el compilador debería tener suficiente contexto para saber que reordenar es peligroso. Cualquier sugerencia para hacer este código simple más la prueba de la bala es bienvenida.

El compilador es libre de asumir char * stream y struct iw_event * iwe point para separar áreas de memoria, debido a un estricto aliasing.

Que es cierto y que no es el problema del que me estoy quejando.

(Nota con retrospectiva: este código está bien, pero la implementación de Linux de memcpy fue una macro que lanzó a long * para copiar en trozos más grandes. Con a correctamente definido memcpy, gcc -fstrict-aliasing no se le permite romper este código. Pero significa que necesita asm en línea para definir un núcleo memcpy si su compilador no sabe cómo convertir un bucle de copia de bytes en asm eficiente, que era el caso de gcc antes de gcc7)

Y el comentario de Linus Torvald sobre lo anterior:

[7] Jean Tourrilhes escribió:: >

Me parece un error del compilador...

¿Por qué crees que el núcleo usa "-fno-strict-aliasing"?

La gente del CCG está más interesada en tratar de averiguar qué puede ser permitido por las especificaciones de c99 que sobre hacer que las cosas realmente funcionen. El aliasing código en particular no es ni siquiera vale la pena habilitar, simplemente no es es posible decirle sanamente a gcc cuando algunas cosas pueden alias.

Algunos usuarios se han quejado de que cuando el siguiente código es compilado sin el-fno-strict-aliasing, el orden de la escritura y memcpy está invertido (lo que significa que un len falso es mem-copiado en el flujo).

El "problema" es que insertamos memcpy (), momento en el que gcc no se preocupan por el hecho de que puede alias, por lo que sólo va a volver a ordenar todo y reclamar que es por su propia culpa. Aunque no hay cuerda una manera de contárselo al CCG.

Traté de conseguir una manera sana hace unos años, y los desarrolladores de gcc realmente no le importaba el mundo real en esta área. Me sorprendería si eso había cambiado, a juzgar por la respuestas que ya he visto.

No voy a molestarme en luchar contra ello.

Linus

Http://www.mail-archive.com/[email protected]/msg01647.html :

El aliasing basado en tipos es estúpido. Es tan increíblemente estúpido que ni siquiera es gracioso. Está roto. Y el CCG tomó la noción rota, y la hizo más al hacerla una cosa" conforme a la letra de la ley " que no tiene sentido.

...

Lo sé para un hecho que gcc reordenaría accesos de escritura que fueran claramente a (estáticamente) la misma dirección. Gcc pensaría de repente que

unsigned long a;

a = 5;
*(unsigned short *)&a = 4;

Podría ser reordenado para establecerlo en 4 primero (porque claramente no se alias - leyendo el estándar), y luego porque ahora la asignación de 'a=5' fue más tarde, la asignación de 4 podría ser elidida por completo! Y si alguien se queja de que el compilador está loco, la gente del compilador diría " nyaah, nyaah, las normas que la gente dijo podemos hacer esto", sin absolutamente ninguna introspección para preguntar si tenía algún SENTIDO.

 24
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-06-18 19:44:34

SWIG genera código que depende de que el alias estricto esté desactivado, lo que puede causar todo tipo de problemas.

SWIGEXPORT jlong JNICALL Java_com_mylibJNI_make_1mystruct_1_1SWIG_12(
       JNIEnv *jenv, jclass jcls, jint jarg1, jint jarg2) {
  jlong jresult = 0 ;
  int arg1 ;
  int arg2 ;
  my_struct_t *result = 0 ;

  (void)jenv;
  (void)jcls;
  arg1 = (int)jarg1; 
  arg2 = (int)jarg2; 
  result = (my_struct_t *)make_my_struct(arg1,arg2);
  *(my_struct_t **)&jresult = result;              /* <<<< horror*/
  return jresult;
}
 7
Author: paleozogt,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:32:26

Gcc, aliasing y arrays de longitud variable 2-D: El siguiente código de ejemplo copia una matriz 2x2:

#include <stdio.h>

static void copy(int n, int a[][n], int b[][n]) {
   int i, j;
   for (i = 0; i < 2; i++)    // 'n' not used in this example
      for (j = 0; j < 2; j++) // 'n' hard-coded to 2 for simplicity
         b[i][j] = a[i][j];
}

int main(int argc, char *argv[]) {
   int a[2][2] = {{1, 2},{3, 4}};
   int b[2][2];
   copy(2, a, b);    
   printf("%d %d %d %d\n", b[0][0], b[0][1], b[1][0], b[1][1]);
   return 0;
}

Con ccg 4.1.2 en CentOS, obtengo:

$ gcc -O1 test.c && a.out
1 2 3 4
$ gcc -O2 test.c && a.out
10235717 -1075970308 -1075970456 11452404 (random)

No se si esto es generalmente conocido, y no se si esto es un error o una característica. No puedo duplicar el problema con gcc 4.3.4 en Cygwin, por lo que puede haber sido arreglado. Algunas soluciones:

  • Use __attribute__((noinline)) para copy().
  • Utilice el conmutador gcc -fno-strict-aliasing.
  • Cambia el tercer parámetro de copy() de b[][n] a b[][2].
  • No use -O2 o -O3.

Notas adicionales:

  • Esta es una respuesta, después de un año y un día, a mi propia pregunta (y estoy un poco sorprendido de que solo haya otras dos respuestas).
  • Perdí varias horas con esto en mi código real, un filtro Kalman. Cambios aparentemente pequeños tendrían efectos drásticos, tal vez debido al cambio de inserción automática de gcc (esto es una suposición; estoy todavía incierto). Pero probablemente no califica como una historia de terror .
  • Sí, sé que no escribirías copy() así. (Y, como un aparte, me sorprendió un poco ver que gcc no desenrolló el doble bucle.)
  • No hay interruptores de advertencia gcc, incluir -Wstrict-aliasing=, hizo nada aquí.
  • Los arrays de longitud variable 1-D parecen estar bien.

Actualizar: Lo anterior no responde realmente a la pregunta del OP, ya que él (i. e.) estaba preguntando sobre casos en los que el aliasing estricto' legítimamente ' rompió su código, mientras que lo anterior parece ser un error de compilador de variedad de jardín.

Lo informé a GCC Bugzilla, pero no estaban interesados en el viejo 4.1.2, a pesar de que (creo) es la clave para el RHEL5 de 1 1 mil millones. No ocurre en 4.2.4 arriba.

Y tengo un ejemplo un poco más simple de un error similar, con una sola matriz. El código:

static void zero(int n, int a[][n]) {
   int i, j;
   for (i = 0; i < n; i++)
   for (j = 0; j < n; j++)
      a[i][j] = 0;
}

int main(void) {
   int a[2][2] = {{1, 2},{3, 4}};
   zero(2, a);    
   printf("%d\n", a[1][1]);
   return 0;
}

Produce los resultados:

gcc -O1 test.c && a.out
0
gcc -O1 -fstrict-aliasing test.c && a.out
4

Parece que es la combinación -fstrict-aliasing con -finline que causa el error.

 4
Author: Joseph Quinsey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-30 20:41:49

El siguiente código devuelve 10, bajo gcc 4.4.4. Nada malo con la unión método o gcc 4.4.4?

int main()
{
  int v = 10;

  union vv {
    int v;
    short q;
  } *s = (union vv *)&v;

  s->v = 1;

  return v;
}
 2
Author: user470617,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-10-09 14:58:59

Aquí está el mío:

Http://forum.openscad.org/CGAL-3-6-1-causing-errors-but-CGAL-3-6-0-OK-tt2050.html

Causó que ciertas formas en un programa CAD se dibujaran incorrectamente. gracias a Dios por el trabajo de los líderes del proyecto en la creación de un conjunto de pruebas de regresión.

El error solo se manifestó en ciertas plataformas, con versiones anteriores de GCC y versiones anteriores de ciertas bibliotecas. y luego solo con-O2 activado. -fno-estricto-aliasing lo resolvió.

 2
Author: don bright,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-23 02:33:25

La regla de Secuencia Inicial Común de C solía interpretarse como que lo hace posible escribir una función que podría trabajar en la porción principal de a amplia variedad de tipos de estructura, siempre que comiencen con elementos de coincidencia tipo. Bajo C99, la regla se modificó para que solo se aplicara si la estructura los tipos involucrados eran miembros de la misma unión cuya declaración completa era visible en el punto de uso.

Los autores del ccg insisten en que el idioma en cuestión solo es aplicable si los accesos se realizan a través del tipo unión, sin perjuicio de los hechos que:

  1. No habría ninguna razón para especificar que la declaración completa debe ser visible si los accesos tuvieran que realizarse a través del tipo de unión.

  2. Aunque la regla de la CEI se describió en términos de sindicatos, su principal la utilidad radica en lo que implica sobre la forma en que las estructuras diseñado y accedido. Si S1 y S2 fueran estructuras que compartieran a CEI, no habría manera de que una función que acepta un puntero a un S1 y un S2 de una fuente externa podría cumplir con las reglas CIS de C89 sin permitir que el mismo comportamiento sea útil con punteros a estructuras que no estaban realmente dentro de un objeto union; especificando CIS por lo tanto, el apoyo a las estructuras habría sido redundante, dado que ya especificado para los sindicatos.

 2
Author: supercat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-19 22:29:27