Tanto GCC como Clang tienen integrado UndefinedBehaviorSanitizer. Una de esas comprobaciones, alignment
, se puede habilitar con -fsanitize=alignment
. Emitirá código para verificar la alineación del puntero en tiempo de ejecución y cancelará si los punteros no alineados no tienen referencia.
Consulte la documentación en línea en:
- https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html
Es complicado y no lo he hecho personalmente, pero creo que puedes hacerlo de la siguiente manera:
Las CPU x86_64 (específicamente he comprobado Intel Corei7 pero supongo que otras también) tienen un contador de rendimiento MISALIGN_MEM_REF que contrarresta las referencias de memoria desalineadas.
Entonces, antes que nada, puede ejecutar su programa y usar la herramienta "perf" en Linux para obtener un recuento de la cantidad de accesos desalineados que ha realizado su código.
Un truco más complicado e interesante sería escribir un módulo de kernel que programe el contador de rendimiento para generar una interrupción en el desbordamiento y hacer que se desborde la primera carga/almacenamiento no alineado. Responda a esta interrupción en su módulo kernel pero enviando una señal a su proceso.
De hecho, esto convertirá el x86_64 en un núcleo que no admite el acceso no alineado.
Sin embargo, esto no será simple:además de su código, las bibliotecas del sistema también usan accesos no alineados, por lo que será complicado separarlos de su propio código.
Acabo de leer la pregunta ¿El acceso a la memoria no alineado siempre causa errores de bus? que se vinculó al artículo de Wikipedia Error de segmentación.
En el artículo, hay un maravilloso recordatorio de las banderas poco comunes del procesador Intel AC, también conocido como Comprobación de alineación.
Y he aquí cómo habilitarlo (del ejemplo de error de bus de Wikipedia, con un error de clobber de zona roja corregido para x86-64 System V, por lo que es seguro en Linux y MacOS, y convertido desde asm básico, que nunca es una buena idea dentro de las funciones:desea que los cambios en AC se ordenen con los accesos a la memoria.
#if defined(__GNUC__)
# if defined(__i386__)
/* Enable Alignment Checking on x86 */
__asm__("pushf\n orl $0x40000,(%%esp)\n popf" ::: "memory");
# elif defined(__x86_64__)
/* Enable Alignment Checking on x86_64 */
__asm__("add $-128, %%rsp \n" // skip past the red-zone, in case there is one and the compiler has local vars there.
"pushf\n"
"orl $0x40000,(%%rsp)\n"
"popf \n"
"sub $-128, %%rsp" // and restore the stack pointer.
::: "memory"); // ordered wrt. other mem access
# endif
#endif
Una vez habilitado, funciona de manera muy similar a la configuración de alineación ARM en /proc/cpu/alignment
, vea la respuesta ¿Cómo atrapar el acceso a la memoria no alineado? para ejemplos.
Además, si usa GCC, le sugiero que habilite -Wcast-align
advertencias Al construir para un objetivo con requisitos estrictos de alineación (ARM, por ejemplo), GCC informará las ubicaciones que podrían conducir a un acceso a la memoria no alineado.
Pero tenga en cuenta que el asm escrito a mano de libc para memcpy y otras funciones aún realizarán accesos no alineados, por lo que configurar AC a menudo no es práctico en x86 (incluido x86-64). GCC a veces emitirá asm que realiza accesos no alineados incluso si su fuente no lo hace, p. como una optimización para copiar o poner a cero dos elementos de matriz adyacentes o miembros de estructura a la vez.