alineación de memoria dentro de las estructuras gcc

Estoy portando una aplicación a una plataforma ARM en C, la aplicación también se ejecuta en un procesador x86 y debe ser compatible con versiones anteriores.

Ahora estoy teniendo algunos problemas con la alineación de variables. He leído el manual de gcc para __attribute__((aligned(4),packed)) Interpreto lo que se dice ya que el inicio de la estructura está alineado con la delimitación de 4 bytes y el interior permanece intacto debido a la statement empaquetada.

originalmente tuve esto, pero ocasionalmente se coloca desalineado con el límite de 4 bytes.

 typedef struct { unsigned int code; unsigned int length; unsigned int seq; unsigned int request; unsigned char nonce[16]; unsigned short crc; } __attribute__((packed)) CHALLENGE; 

así que lo cambio a esto.

 typedef struct { unsigned int code; unsigned int length; unsigned int seq; unsigned int request; unsigned char nonce[16]; unsigned short crc; } __attribute__((aligned(4),packed)) CHALLENGE; 

El entendimiento que mencioné anteriormente parece ser incorrecto, ya que tanto la estructura ahora está alineada con un límite de 4 bytes, y los datos internos están ahora alineados con un límite de cuatro bytes, pero debido a la endianess, el tamaño de la estructura ha aumentado en tamaño de 42 a 44 bytes. Este tamaño es crítico ya que tenemos otras aplicaciones que dependen de que la estructura tenga 42 bytes.

Podrían algunos describirme cómo realizar la operación que requiero. Cualquier ayuda es muy apreciada.

Si sizeof(yourstruct) de sizeof(yourstruct) 42 bytes, estarás a punto de ser mordido por un mundo de supuestos no portátiles. No ha dicho para qué sirve esto, pero parece probable que la endiancia del contenido de la estructura también importa, por lo que también puede haber un desajuste con el x86 allí.

En esta situación, creo que la única forma segura de hacer frente es usar unsigned char[42] en las partes en las que importa. Comience por escribir una especificación precisa de qué campos exactamente son en este bloque de 42 bytes y qué endian, luego use esa definición para escribir algún código para traducir entre eso y una estructura con la que pueda interactuar. Es probable que el código sea un código de serialización todo en uno (también conocido como clasificación), o un grupo de getters y setters.

Esta es una razón por la cual la lectura de estructuras completas en lugar de memberwise falla y debe evitarse.

En este caso, empaquetar y alinear a 4 significa que habrá dos bytes de relleno. Esto sucede porque el tamaño debe ser compatible para almacenar el tipo en una matriz con todos los elementos aún alineados en 4.

Me imagino que tienes algo como:

 read(fd, &obj, sizeof obj) 

Como no desea leer esos 2 bytes de relleno que pertenecen a datos diferentes, debe especificar el tamaño de forma explícita:

 read(fd, &obj, 42) 

Que puedes mantener mantenible:

 typedef struct { //... enum { read_size = 42 }; } __attribute__((aligned(4),packed)) CHALLENGE; // ... read(fd, &obj, obj.read_size) 

O bien, si no puede usar algunas características de C ++ en su C:

 typedef struct { //... } __attribute__((aligned(4),packed)) CHALLENGE; enum { CHALLENGE_read_size = 42 }; // ... read(fd, &obj, CHALLENGE_read_size) 

En la próxima oportunidad de refactorización, le sugiero encarecidamente que comience a leer cada miembro individualmente, lo que puede encapsularse fácilmente dentro de una función.

¿Cuál es tu verdadero objective?

Si se trata de manejar datos que están en un archivo o en un formato particular, lo que debes hacer es escribir algunas rutinas de marshaling / serialización que muevan los datos entre la estructura del comstackdor que representa cómo quieres tratar los datos dentro del progtwig y una matriz char que trata de cómo se ven los datos en el cable / archivo.

Entonces, todo lo que necesita tratarse cuidadosamente y posiblemente tener un código específico de plataforma son las rutinas de clasificación. Y puede escribir algunas pruebas de unidades agradables y desagradables para asegurarse de que los datos recostackdos lleguen y salgan de la estructura de forma adecuada, sin importar la plataforma a la que deba dirigirse hoy y en el futuro.

Supongo que el problema es que 42 no es divisible por 4, por lo que se desalinean si colocas varias de estas estructuras una detrás de otra (por ejemplo, asigna memoria para varias de ellas, determinando el tamaño con sizeof ). Tener el tamaño de 44 obliga a la alineación en estos casos como lo solicitó. Sin embargo, si el desplazamiento interno de cada miembro de la estructura sigue siendo el mismo, puede tratar la estructura de 44 bytes como si fuera de 42 bytes (siempre que tenga cuidado de alinear los datos siguientes en el límite correcto).

Un truco para probar podría ser colocar ambas estructuras dentro de un solo tipo de unión y solo usar una versión de 42 bytes desde cada unión.

He estado moviendo estructuras hacia adelante y hacia atrás desde Linux, Windows, Mac, C, Swift, Assembly, etc.

El problema NO es que no se pueda hacer, el problema es que no puedes ser flojo y debes entender tus herramientas.

No veo por qué no puedes usar:

 typedef struct { unsigned int code; unsigned int length; unsigned int seq; unsigned int request; unsigned char nonce[16]; unsigned short crc; } __attribute__((packed)) CHALLENGE; 

Las probabilidades de estar en un “mundo de dolor” son nulas si entiendes lo que está pasando con ambos.

Finalmente, no puedo entender cómo obtienes 42 o 44. Int es 4 de nuestros 8 bytes (dependiendo del comstackdor). Eso pone el número en 16 + 16 + 2 = 34 o 32 + 16 + 2 = 50 – asumiendo que está realmente lleno.

Como dije, conocer tus herramientas es parte de tu problema.

Como estoy usando Linux, he encontrado que por echo 3 > /proc/cpu/alignment me emitirá una advertencia y solucionará el problema de alineación. Esto es una solución pero es muy útil para ubicar las estructuras que no están correctamente alineadas.