2010-03-25 7 views

risposta

7

La convenzione di chiamata deve essere quella in cui il chiamante cancella gli argomenti dallo stack (perché il destinatario non sa cosa verrà passato).

Ciò non corrisponde necessariamente a ciò che Microsoft chiama "__cdecl". Per esempio, su uno SPARC, normalmente passerà gli argomenti nei registri, perché è così che SPARC è progettato per funzionare: i suoi registri agiscono fondamentalmente come uno stack di chiamate che viene riversato nella memoria principale se le chiamate diventano abbastanza profonde non entreranno più nel registro.

Anche se ne sono meno certo, mi aspetterei all'incirca lo stesso su IA64 (Itanium) - ha anche un enorme set di registri (un paio di centinaia se la memoria serve). Se non sbaglio, è un po 'più permissivo su come usi i registri, ma mi aspetterei che venga usato in modo simile almeno per la maggior parte del tempo.

Perché questo ti interessa? Il punto di utilizzo di stdarg.h e delle sue macro è quello di nascondere le differenze nella convenzione di chiamata dal codice, in modo che possa lavorare con argomenti variabili in modo portabile.

Modifica, in base ai commenti: Ok, ora capisco cosa stai facendo (almeno quanto basta per migliorare la risposta). Dato che hai già (apparentemente) un codice per gestire le variazioni nell'ABI predefinito, le cose sono più semplici. Ciò lascia solo la questione se le funzioni variadic usino sempre "l'ABI di default", qualunque cosa accada per la piattaforma in questione. Con "stdcall" e "default" come le uniche opzioni, penso che la risposta sia sì. Solo per esempio, su Windows, wsprintf e wprintf interrompi la regola empirica e usa la convenzione di chiamata di cdecl invece di stdcall.

+1

Il tuo commento su SPARC si applica a molte se non a tutte le architetture RISC. Dal momento che i registri sono così numerosi, il compilatore usa solo quelli per passare argomenti. Per non parlare, sono più veloci della memoria da un bel po ', esp. dato che probabilmente stai trasmettendo dati che sono già nei registri (anche se penso che sarà necessario inserirli nello stack per ripristinarli se ne avrà bisogno in seguito, ma ...). Ad ogni modo, so che questo è il modo in cui funziona su MIPS e sono abbastanza certo che PPC funzioni allo stesso modo. –

+2

* "Perché questo ti interessa?" * Ben sta lavorando su js-ctypes per Mozilla. Puoi vedere il contesto esatto in https://bugzilla.mozilla.org/show_bug.cgi?id=554790 (cerca "idea pazza") e puoi alcuni documenti che spiegano js-ctypes su https: //developer.mozilla. org/it/JavaScript_code_modules/ctypes.jsm –

+0

Inutile dire che js-ctypes è disponibile solo per codice applicativo e componenti aggiuntivi, non per pagine web. –

0

Intende "piattaforme supportate da MSVC" o come regola generale? Anche se ci si limita alle piattaforme supportate da MSVC, esistono ancora situazioni come IA64 e AMD64 in cui esiste solo una convenzione di chiamata "una" e quella convenzione di chiamata si chiama __stdcall, ma non è certo la stessa __stdcall si ottiene su x86

0

AFAIK, la diversità delle convenzioni di chiamata è unica per DOS/Windows su x86. La maggior parte delle altre piattaforme aveva compilatori dotati di sistema operativo e standardizzazione la convenzione.

2

il modo più definitivo che è possibile determinare questo è quello di analizzare il Callin g convenzioni. Per le funzioni variadic al lavoro, la vostra convenzione di chiamata ha bisogno di un paio di attributi:

  • Il chiamato deve essere in grado di accedere ai parametri che non fanno parte della lista di argomenti variabile da un offset fisso dalla cima della pila . Ciò richiede che il compilatore spingere i parametri nello stack da destra a sinistra. (Questo include cose come il primo parametro printf, specifica di formato. Inoltre, l'indirizzo della lista di argomenti variabile stessa devono essere derivati ​​da una posizione nota.)
  • Il chiamante deve essere responsabile della rimozione dei parametri fuori impilare una volta che la funzione è tornato perché solo il compilatore, mentre genera il codice per il chiamante, sa quanti parametri sono stati inserito nello stack in primo luogo. La stessa funzione variadica non ha questa informazione.

stdcall non funziona perché il callee è responsabile dello scoppiare i parametri fuori dallo stack. Nei vecchi giorni Windows a 16 bit, pascal non funzionava perché spingeva i parametri nello stack da sinistra a destra.

Naturalmente, come accennato alle altre risposte, molte piattaforme non offrono alcuna scelta in termini di convenzione di chiamata, rendendo questa domanda irrilevante per quelle.

1

Si consideri la seguente funzione su un sistema x86:

vuoto qualcosa __stdcall (char *, ...);

La funzione si dichiara come __stdcall, che è una convenzione callee-clean. Ma una funzione variadica non può essere cancellata dal callee dato che il chiamato non sa quanti parametri sono stati passati, quindi non sa quanti ne dovrebbe pulire.

Il compilatore Microsoft Visual Studio C/C++ risolve questo conflitto convertendo silenziosamente la convenzione di chiamata a __cdecl, che è l'unica convenzione di chiamata variadic supportata per le funzioni che non accettano un parametro nascosto.

Perché questa conversione avviene in modo silenzioso anziché generare un avviso o un errore?

La mia ipotesi è che è rendere meno fastidiose le opzioni del compilatore/Gr (imposta la convenzione di chiamata predefinita su __fastcall) e/Gz (imposta la convenzione di chiamata predefinita su __stdcall).

La conversione automatica delle funzioni variadiche in __cdecl significa che è possibile aggiungere l'opzione della riga di comando/Gr o/Gz alle opzioni del compilatore e tutto verrà comunque compilato ed eseguito (solo con la nuova convenzione di chiamata).

Un altro modo di vedere questo non è da pensare al compilatore come conversione __stdcall variadic a __cdecl ma semplicemente dicendo “per le funzioni variadic, __stdcall è chiamante-pulito.”

click here

Problemi correlati