¿Cómo haría para hacer que una unión de estructuras/uniones anidadas sea compatible con las convenciones de 64 bits en C?

Entonces, esto podría ser complicado, pero aquí va. Estoy trabajando en un antiguo proyecto de C, llamado PMML, el lenguaje práctico de macros de música. Es un lenguaje de programación para midi de finales de los 90. Tengo el código disponible en github, ( https://github.com/arfy8820/pmml ), escrito originalmente por otro autor. He sido un aficionado a C durante años, pero este código está ampliando mi (ciertamente limitado) conjunto de habilidades. En primer lugar, ¡este código parece estar escrito en estilo K&R! Hace que sea difícil averiguar qué funciones tienen prototipos. El autor original usó algo de magia de preprocesador para adaptar el código tanto a no ANSI como a ANSI C.

Pero ahora, al evento principal. Me las arreglé para llevar el código al punto en que se compila en sistemas de 32 y 64 bits, sin embargo, en un sistema de 64 bits, como mi Mac Apple M1, que no tiene forma de ejecutar el código como 32 bits , pmml segfaults tan pronto como intente hacer otra cosa que no sea mostrar ayuda. Sí, puedo compilar como 32 bits en cualquier otro sistema/VM que haya probado, pero también me encantaría que esto se ejecute de forma nativa en mi Mac.

Creo que he aislado el problema en su estructura de datos principal, que es una unión de varios tipos diferentes. Como se indica en pmml.h:

/* 
 * Object - the data type of the PMML
 *   Each macro is associated with an object.
 *   Each array element is an object.
 *   Each object occupies 8 bytes.
 */

Es la última regla, cada objeto tiene 8 bytes, que se rompe en condiciones de 64 bits. Aquí está mi comprensión de cómo funciona esta unión.

Primero, algunas cosas que debe saber anteriormente en pmml.h.

/********************************************************************
 * Definition of byte order
 ********************************************************************/
#ifndef PMML_BIG_ENDIAN
#  ifdef PMML_LITTLE_ENDIAN
#    define PMML_BIG_ENDIAN  0
#  else
#    if defined(mc68000) || defined(sparc) || defined(MIPSEB)
#      define PMML_BIG_ENDIAN  1
#    else
#      if defined(i386) || defined(__x86_64__) || defined(__arm64) || defined(MIPSEL) || defined(alpha)
#        define PMML_BIG_ENDIAN  0
#      else
         ... Either PMML_BIG_ENDIAN or PMML_LITTLE_ENDIAN must be defined.
#      endif
#    endif
#  endif
#endif

¡Sí, estos códigos intentan ejecutarse en muchos lugares!

Typedefs útiles para saber.

typedef unsigned char  uchar;
typedef int   PmmlInt;
typedef double PmmlFloat;

Ahora, para la unión.

typedef union object {
    struct {
#if PMML_BIG_ENDIAN
    unsigned short  type;   /* O_XXX */
    short  pad1;
    short  pad2;
    short  pad3;
#else
    short  pad1;
    short  pad2;
    short  pad3;
    unsigned short  type;   /* O_XXX */
#endif
    } _t; // 4 shhorts, 8 bytes on both 32 and 64-bit
    Rational  r; // Rational is defined as 4 shorts, so again, 8 bytes regardless of bit-width

PmmlFloat  fpval;       /* for O_FLOAT */ 
// PmmlFloat is a double, so again, 8 bytes.

Ahora, comienza la diversión.

    struct {
#if PMML_BIG_ENDIAN
    long   pad; // only for big endian systems, 4 bytes.
#endif
    union object_data {
        PmmlInt  val;   /* for O_INT */
        char   *str;    /* for O_STRING */
        struct array  *ap;  /* for O_ARRAY */
        struct token  *tp;  /* for O_STOKENS or O_WTOKENS */
    } _u;
    } _d;

Aquí, creo, está la clave de esta unión. Debe haber una razón para que los sistemas big-endian tengan esa almohadilla larga al comienzo de la estructura. Para mí, indica que se espera que la unión anidada aquí ocupe 4 bytes. Todavía estamos dentro de la unión de objetos principal. Donde se rompe la regla de 8 bytes son los punteros. en sistemas de 32 bits, todo está bien y los punteros int y 3 ocupan el mismo espacio. sin embargo, en 64 bits? esa suposición se rompe.

Entonces, pensamientos que tuve que continuar.

  • ¿Podría simplemente rellenar todo en la unión si el código se ejecuta en un arco de 64 bits, por ejemplo, 12 o 16 bytes?
  • He jugado brevemente con simplemente convertir todo en estructuras, pero eso tampoco funcionó del todo.

Perdón por la extensión, pero espero que esta sea suficiente información para ayudarnos a determinar qué se podría hacer aquí.

¡Gracias! arrogante

Answer