2010-09-29 13 views
8

Tengo un proyecto que consiste en un grupo de módulos cargados dinámicamente. Originalmente, todo estaba siempre construido con MSVC 2003, pero últimamente he estado trabajando para que funcione con GCC. Todo ha ido bastante bien, excepto por un problema. Para el código de 64 bits, GCC y MSVC no están de acuerdo con lo que es va_list. Para 32 bits, las cosas parecen estar bien. El problema que causa la discrepancia de 64 bits es cuando un módulo creado con un compilador tiene una función pública con un parámetro va_list y esa función se llama desde un módulo creado por el otro compilador.Coincidencia de tipos de va_list entre compiladores

La especificación no dice nada acerca de lo que un va_list es decir, fuera de Sección 7.15 argumentos variables <stdarg.h>, párrafo 3:

El tipo declarado es

va_list

que es un tipo de objeto adecuado para contener la información que necesitan las macros va_start, va_arg, va_end, y va_copy.

En ese párrafo sólo significa que se trata de todas las cosas depende del compilador - es así, ¿hay una manera de hacer que estos dos compiladores están de acuerdo en el contenido de un 64-bit va_list? Para un impacto mínimo en mi sistema, hacer que GCC coincida con el MSVC va_list sería lo mejor, pero tomaré cualquier solución que pueda obtener.

Gracias por ayudar!

Editar:

Hice algunas pruebas de 32 bits, y no tengo problemas allí también, lo que me sorprendió ya que hay diferencias supuestamente ABI entre las plataformas Intel de 32 bits. La base de código que estoy usando MSVC define todas las macros de función variadic como:

typedef char *va_list; 
#define intsizeof(n) ((sizeof(n) + sizeof(int) - 1) &~(sizeof(int) - 1)) 
#define va_start(ap, v) (ap = (va_list)&(v) + intsizeof(v)) 
#define va_arg(ap, t) (*(t *) ((ap += intsizeof(t)) - intsizeof(t))) 
#define va_end(ap)  (ap = (va_list)0) 

he simplificado un poco del proyecto real, pero este es el código que estaba usando para mi prueba. Con GCC, este código definitivamente no está obteniendo mis argumentos correctamente. Tal vez es solo un error, como Zack sugiere a continuación?

de nuevo Edit:

consigo resultados de trabajo para la aplicación de prueba de 32 bits que sigue con -O0, -O0, y -O2, pero no -O3, -Os y -Oz:

typedef char *va_list; 
#define intsizeof(n) ((sizeof(n) + sizeof(int) - 1) &~(sizeof(int) - 1)) 
#define va_start(ap, v) (ap = (va_list)&(v) + intsizeof(v)) 
#define va_arg(ap, t) (*(t *) ((ap += intsizeof(t)) - intsizeof(t))) 
#define va_end(ap)  (ap = (va_list)0) 

int printf(const char *format, ...); 

int f(int n, ...) 
{ 
    int r = 0; 
    va_list ap; 

    va_start(ap, n); 
    while (n--) 
    r = va_arg(ap, int); 
    va_end(ap); 

    return r; 
} 

int main(int argc, char **argv) 
{ 
    int r; 

    r = f(1, 1, 2, 3, 4, 5); 
    printf("%x\n", r); 

    r = f(2, 1, 2, 3, 4, 5); 
    printf("%x\n", r); 

    r = f(3, 1, 2, 3, 4, 5); 
    printf("%x\n", r); 

    r = f(4, 1, 2, 3, 4, 5); 
    printf("%x\n", r); 

    r = f(5, 1, 2, 3, 4, 5); 
    printf("%x\n", r); 

    return 0; 
} 
+0

Uh. Copiaste las definiciones 'va_ *' del '' de MSVC en un archivo que luego compiló con GCC, ¿o sí? Porque eso definitivamente no funcionará, y no te dice nada útil. Debe usar absolutamente el '' de GCC para definir funciones variadas compiladas con GCC (y MSVC para MSVC) o su código * será * mal compilado. – zwol

+0

Lo que debe hacer para esta prueba es mover 'f' a su propio archivo, reemplazar todas las definiciones de mano de' va_ * 'con' #include ', poner" 'extern int f (int n, ...) ; 'above' main' en ese archivo, compilar uno con GCC y el otro con MSVC, y vincular los dos archivos de objeto. * Eso * debería funcionar en cualquier dirección (MSVC llama a las llamadas GCC o GCC MSVC) en x32 o x64. – zwol

+0

Sí, funcionó bien al apagar la línea interior o al colocarla en una unidad de compilación separada. De todos modos, es un problema completamente separado de los tipos va_list que no coinciden, que como dices, es un error con el compilador –

Respuesta

5

Dado que MSVC define Win64 ABI, ha encontrado un error en GCC. Por favor repórtalo en GCC bugzilla.

+0

Una 'va_list' no es parte de la ABI ¿verdad? De todos modos, mi plan a largo plazo es usar clang/llvm, así que supongo que debería verificarlo más temprano que tarde. Es muy posible que GCC esté bien en versiones más recientes: en este caso estoy atrapado en una especie de GCC antiguo "especial". –

+0

Positivamente 'va_list' es parte de ABI. El propósito del ABI es garantizar que todos los compiladores para un objetivo determinado CPU + OS generen código compatible, y eso incluye funciones variadas. ... He visto un par de parches de soporte de Win64 para GCC muy recientemente, así que esto puede funcionar con la versión actual o al menos con el árbol de desarrollo. – zwol

+0

@Zack, las funciones variadic funcionan bien, simplemente pasar la 'va_list' en sí no funciona. Gracias por el control de cordura. Voy a hacer más pruebas y ver qué sucede. –

0

porque hay no hay un estándar sobre cómo va_args tiene que ser handlded, si necesita que esta funcionalidad sea consistente en una plataforma compilada cruzada, probablemente sería mejor que imprima su propia versión. No lo hicimos y hemos sufrido quemaduras varias veces recientemente como resultado al admitir objetivos adicionales para nuestra base de código.Aunque me gustaría equivocarme si otros tienen una mejor solución :)

+0

OK, ¿cómo puedo ¿rodar mi propia versión? Escribí uno que funciona para IA32, pero gcc no parece derramar los argumentos de la manera que quiero para Win64. –

0

Prueba a ejecutarlo a través de un depurador de nivel de conjunto como ollydbg, ya que tu problema podría no estar en los va_args, sino en la forma en que está el compilador esperando que se pasen los argumentos (gcc podría estar esperándolos en el formato de Linux, mientras que msvc está usando win64 __fastcall), si acaso esto dará un poco más de luz a la situación. Otro enfoque más bien raro es tratar de jugar con las definiciones utilizadas para los argumentos de 64 bits, como la importación de las macros msvc en el encabezado gcc (por supuesto, use una copia local del proyecto), vea si eso soluciona algo.

+0

Llamadas a funciones variables entre módulos construidos con los diferentes compiladores, por lo que el ABI está haciendo coincidir arriba. Es solo el a Implementación interna virtual de la 'va_list' que me está mordiendo. –

2

Dado que no parece haber un ABI para va_list (o al menos MSVC y GCC no están de acuerdo con un ABI), es probable que necesite ordenar esos parámetros usted mismo.

La manera más directa en que puedo pensar para solucionar este problema de la cabeza es ordenar los parámetros variables en un bloque de memoria asignado dinámicamente y pasar un puntero a ese bloque.

Por supuesto, esto tiene el inconveniente de cambiar por completo la interfaz a las funciones que actualmente usan va_args.

+0

Pero tendría que saber cuántos parámetros * hay * para pegarlos en un bloque dinámico. Eso significa que cada función que quiera hacer 'va_start' y luego llamar a otra función con' va_list' tiene que saber cómo identificarlas; en el caso de las funciones 'printf', eso significa una gran cantidad de análisis adicionales, donde el el punto entero de pasar el 'va_list' es consolidar todo ese formato cadena de análisis en una sola función (como' vsprintf'). –

-1

¿Qué tipos estás utilizando? Estrictamente hablando, WinXX solo define el comportamiento para caracteres, cadenas, punteros y enteros para varargs (see documentation for wsprintf in user32.dll). Por lo tanto, si transfiere valores o estructuras de punto flotante, los resultados no están especificados técnicamente para la plataforma de Windows.

+0

No hay punto flotante de ningún tipo en este proyecto, por lo que debería estar bien. Me sorprendería si se lanzaran estructuras, pero puedo investigar eso. En general, solo se imprimen tipos y cadenas enteros. –

+0

Creo que esta respuesta es simplemente incorrecta, por cierto ... que la versión 'user32.dll' de' wsprintf' no menciona "% f" y los amigos en su documentación no * significan * que no se puede Pase el punto flotante a través de varargs en win32 en general. Y el estándar C requiere pasar tanto el punto flotante como las estructuras a través de varargs para funcionar. – zwol

+0

@Zack, actualicé mi respuesta para reflejar que esto no está especificado para la plataforma de Windows. Significa que no debe pasar esos tipos a wsprintf (...) que es una función user32.dll. No significa nada para un lenguaje implementado sobre Windows, como C o C++. Entonces, si quiere compatibilidad entre los compiladores, debe apegarse a los tipos que la plataforma le obliga a admitir. En este caso, tiene la suerte de que wsprintf se haya exportado desde user32.dll. – MSN

Cuestiones relacionadas