la differenza è molto semplice, ma devi avere già chiaro in testa come funziona a livello assembly il passaggio di parametri nella chiamata a funzioni.
In generale, sia con chiamate stdcall che cdecl quando chiami una funzione e gli vuoi passare un parametro, quello che fai è "pushare" quel parametro sullo stack, quindi chiami la funzione, all'interno della funzione il parametro è ancora disponibile sullo stack e da li viene prelevato per svolgere i compiti della funzione.
La differenza tra i due consiste in CHI si deve preoccupare di sistemare lo stack dopo che finisce l'esecuzione della funzione e si ritorna al chiamante.
cdecl: è la funzione, all'interno del proprio codice, che si preoccupa di sistemare lo stack togliendo i parametri pushati dal chiamante prima dell'esecuzione della funzione
stdcall: è il chiamante che deve preoccuparsi di togliere dallo stack i parametri una volta tornati dalla funzione
Il modo migliore di vederlo comunque è se ti scrivi un piccolissimo programma in C dove nel main chiami 2 funzioni, una con cdecl e una con stdcall passando ad entrambe un parametro.
Poi vai a debuggare con OllyDbg il tuo programmino e allora vedi esattamente quello che sto dicendo.
Ad esempio:
C++ Programming
int _stdcall funz1(int val)
{
return (val*2);
}
int _cdecl funz2(int val)
{
return (val*3);
}
int main(int argc, char* argv[])
{
int res = funz1(10);
int res2 = funz2(res);
return res2;
}
Se andiamo a vedere con Olly troviamo questo:
funz1 (cdecl):
ASM Programming
PUSH EBP
MOV EBP,ESP
MOV EAX,DWORD PTR SS:[ARG.1]
SHL EAX,1
POP EBP
RETN 4 <--- la funz. ripulisce lo stack togliendo una DWORD, il parametro
funz2 (stdcall):
ASM Programming
PUSH EBP
MOV EBP,ESP
MOV EAX,DWORD PTR SS:[ARG.1]
IMUL EAX,EAX,3
POP EBP
RETN <---- qui NON è la funzione a ripulire
E vediamo il codice nella funzione main, ovvero il chiamante:
(ho lasciato solo la parte che ci interessa)
chiamata a funz1 (cdecl)
ASM Programming
PUSH 0A <-- pushiamo il parametro sullo stack
CALL funz1 <-- dentro la funzione pulisce lo stack
chiamata a funz2 (sdcall)
ASM Programming
PUSH EAX <-- pushiamo il parametro sullo stack
CALL funz2 <-- la funzione dentro NON pulisce lo stack quindi...
ADD ESP,4 <--- deve farlo il chiamante, togliendo una DWORD dallo stack
Finchè sei tu che chiami le tue funzioni nel tuo programma non ha molta imporanza che la chiamata sia cdecl (il default) o che sia stdcall.
L'importanza nasce nel momento in cui chiami funzioni che si trovano in altre librerie o comunque esterne al tuo programma.
Un esempio sono le API di windows, che solitamente usano la chiamata stdcall.
Il problema di chiamare una funzione con la convenzione sbagliata è di ottenere un crash sicuro perchè quello che succede è che lo stack viene corrotto.