2010-09-29 10 views
8

Ho un progetto costituito da un gruppo di moduli caricati in modo dinamico. Originariamente, tutto era sempre stato realizzato con MSVC 2003, ma ultimamente ho lavorato per farlo funzionare con GCC. Tutto è andato abbastanza bene, tranne che per un problema. Per il codice a 64 bit, GCC e MSVC non sono d'accordo su cosa sia uno va_list. Per 32 bit, le cose sembrano andare bene. Il problema delle mancate corrispondenze a 64 bit è quando un modulo creato con un compilatore ha una funzione pubblica con un parametro va_list e tale funzione viene chiamata da un modulo creato dall'altro compiler.Tipi di va_list corrispondenti tra i compilatori

Le specifiche non dice nulla su ciò che un va_list è, al di fuori di Sezione 7.15 argomenti variabili <stdarg.h>, comma 3:

Il tipo dichiarato è

va_list

che è un tipo di oggetto adatto per contenere le informazioni necessarie per le macro va_start, va_arg, va_end, e va_copy.

Questo punto significa che solo questa è tutta roba dipende compilatore - è così, c'è un modo per rendere questi due compilatori d'accordo sul contenuto di un 64-bit va_list? Per il minore impatto sul mio sistema, fare in modo che GCC corrisponda al MSVC va_list sarebbe il migliore, ma prenderò qualsiasi soluzione che possa ottenere.

Grazie per l'aiuto!

Edit:

ho fatto dei test a 32 bit, e non ho problemi anche lì, che mi ha sorpreso in quanto non vi sono presumibilmente differenze tra ABI eventuali piattaforme Intel a 32 bit. La base di codice MSVC sto usando definisce tutte le macro funzione variadic come:

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) 

Ho semplificato un po 'dal vero e proprio progetto, ma questo è il codice che stavo usando per la mia prova. Con GCC, questo codice sicuramente non sta ottenendo correttamente i miei argomenti. Forse è solo un bug, come suggerisce Zack di seguito?

Edit ancora:

ottengo risultati di lavoro per la seguente applicazione di test a 32 bit con -O0, -O0, e -O2, ma non -O3, -Os, e -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. Hai copiato le definizioni 'va_ *' da MSVC '' in un file che hai poi compilato con GCC, è così? Perché sicuramente non funzionerà e non ti dirà niente di utile. È assolutamente necessario utilizzare GCC "" per definire le funzioni variadiche compilate con GCC (e MSVC per MSVC) o il proprio codice * sarà errato. – zwol

+0

Quello che devi fare per questo test è spostare 'f' sul proprio file, sostituire tutte le definizioni di mano di' va_ * 'con' #include ', inserisci" 'extern int f (int n, ...) ; 'sopra' main' in quel file, compila uno con GCC e l'altro con MSVC, e collega i due file oggetto. * Che * dovrebbe funzionare in entrambe le direzioni (MSVC chiama GCC o GCC chiama MSVC) su x32 o x64. – zwol

+0

Sì, ha funzionato bene quando si disattivava l'inlining o quando lo si inseriva in un'unità di compilazione separata, ma è un problema completamente separato dai tipi di va_list non corrispondenti, che, come dici tu, è un bug con il compilatore –

risposta

5

Poiché MSVC definisce l'ABI Win64, è stato rilevato un errore in GCC. Si prega di segnalarlo in GCC bugzilla.

+0

Un 'va_list' non fa parte dell'ABI vero? Ad ogni modo il mio piano a lungo termine è quello di usare clang/llvm, quindi suppongo che dovrei controllarlo prima possibile. È abbastanza probabile che GCC sia ok nelle versioni più recenti - in questo caso sono bloccato su una specie di vecchio GCC "speciale". –

+0

Positivamente 'va_list' fa parte dell'ABI. Lo scopo dell'ABI è di garantire che tutti i compilatori per un dato CPU + SO di destinazione generino un codice compatibile e che includa funzioni variadiche. ... Ho visto un sacco di patch di supporto per Win64 per GCC abbastanza recenti, quindi questo potrebbe funzionare con la versione corrente o almeno con l'albero di sviluppo. – zwol

+0

@Zack, le funzioni variadic funzionano bene - il solo passaggio della 'va_list' stessa non funziona. Grazie per il controllo di sanità mentale - farò qualche altro test e vedrò cosa succede. –

0

perché non c'è nessuno standard su come va_args deve essere gestito, se hai bisogno di questa funzionalità per essere coerente in una piattaforma cross-compilata, probabilmente starai meglio con la tua versione. Non l'abbiamo fatto e sono stati bruciati più volte di recente come risultato nel supportare obiettivi aggiuntivi per il nostro codebase.Mi piacerebbe essere sbagliato anche se altri hanno una soluzione migliore :)

+0

OK, quindi come faccio? rotolo la mia versione? Ne ho scritto uno che funziona per IA32, ma gcc non sembra che stia versando gli argomenti nel modo in cui lo voglio per Win64. –

0

Provate a lanciare attraverso un debugger livello di assieme, come OllyDbg, come il problema potrebbe non essere con i va_args, ma piuttosto con il modo in cui il compilatore è aspettando che gli argomenti siano passati (gcc potrebbe aspettarli nel formato linux, dove msvc sta usando win64 __fastcall), semmai questo darà un po 'più di luce alla situazione. Un altro approccio piuttosto hacky è provare a manipolare le definizioni utilizzate per gli argomenti a 64 bit, come ad esempio l'importazione delle macro di msvc nell'intestazione di gcc (utilizzare ovviamente una copia locale del progetto), vedere se questo rimedi a qualcosa.

+0

Chiamate di funzioni variabili tra i moduli compilati con i diversi compilatori, quindi l'ABI corrisponde up. È solo l'a implementazione interna effettiva del 'va_list' che mi morde. –

2

Poiché non sembra essere un ABI per va_list (o almeno MSVC e GCC non sono d'accordo su un ABI), probabilmente sarà necessario effettuare il marshalling di tali parametri.

Il modo più semplice che posso immaginare per aggirare questo problema in cima alla mia testa è quello di eseguire il marshalling dei parametri delle variabili in un blocco di memoria assegnato in modo dinamico e passare un puntatore a quel blocco.

Ovviamente, questo ha lo svantaggio di cambiare completamente l'interfaccia con le funzioni che stanno attualmente utilizzando va_args.

+0

Ma dovrei sapere quanti parametri ci * sono * per incollarli in un blocco dinamico. Ciò significa che ogni funzione che vuole fare 'va_start' e quindi chiamare un'altra funzione con' va_list' deve sapere come identificarli - nel caso delle funzioni 'printf', significa un sacco di parsing extra, dove il L'intero punto di passaggio di 'va_list' è quello di consolidare tutto il formato di parsing di stringhe in una singola funzione (come' vsprintf'). –

-1

Che tipi stai usando? Strettamente parlando, WinXX definisce solo il comportamento per caratteri, stringhe, puntatori e numeri interi per vararg (see documentation for wsprintf in user32.dll). Quindi, se si passano valori o strutture in virgola mobile, i risultati sono tecnicamente non specificati per la piattaforma Windows.

+0

In questo progetto non c'è alcun punto variabile di alcun tipo, quindi dovrebbe essere ok. Sarei sorpreso se le strutture venissero gettate in giro, ma posso esaminarle. In generale vengono stampati solo i tipi interi e le stringhe. –

+0

Penso che questa risposta sia semplicemente sbagliata, btw ... la versione di 'wsprintf' di' user32.dll' non menziona "% f" e gli amici nella sua documentazione * non * significano che non puoi passare in virgola mobile attraverso varargs su win32 in generale. E lo standard C richiede il passaggio sia di floating point che di strutture tramite vararg per funzionare. – zwol

+0

@Zack, ho aggiornato la mia risposta per riflettere che questo non è specificato per la piattaforma Windows. Significa che non dovresti passare quei tipi a wsprintf (...) che è una funzione user32.dll. Non significa nulla per un linguaggio implementato su Windows, come C o C++. Quindi, se si desidera la compatibilità tra i compilatori, è necessario attenersi ai tipi che la piattaforma impone di supportare. In questo caso, sei abbastanza fortunato che wsprintf è stato esportato da user32.dll. – MSN