2012-01-25 23 views
10

ho smontato un file oggetto (molto probabilmente generato utilizzando l'++ compilatore Visual C) utilizzando DumpBin e vide il seguente pezzo di codice:Perché il compilatore genera questo codice?

...   ... 
mov   dword ptr [ebp-4],eax  // Why save EAX? 
push  dword ptr [ebp+14h] 
push  dword ptr [ebp+10h] 
push  dword ptr [ebp+0Ch] 
push  dword ptr [ebp+8] 
mov   eax,dword ptr [ebp-4]  // Why restore EAX? Did it change at all? 
call  <function> 
...   ... 

Qualcuno potrebbe spiegare perché il registro EAX viene salvato e ripristinato attraverso questi 4 push istruzioni?

+2

Ho visto i compilatori fare cose più stupide di questa ... – Mysticial

+0

@Mysticial: Oh lol ... è la prima volta che ho notato qualcosa del genere. :) Buono a sapersi. – Mehrdad

+7

Forse c'è un ramo nella prima spinta. –

risposta

9

Inoltre, forse è compilato in modalità di rilascio, ma quella variabile è stata contrassegnata come volatile, che indica al compilatore che tale variabile può cambiare senza che lo sappia, quindi è costretta a scrivere/ripristinarlo continuamente su/dallo stack

+0

Ahhhh questo è abbastanza possibile! +1 grazie. – Mehrdad

+0

Non sono sicuro che "volatile" farebbe la differenza qui. 'volatile' appartiene ad una posizione di memoria, ma EAX è un registro; non è possibile contrassegnare un registro come volatile. Quindi 'volatile' spiegherebbe [ebp-4] che viene ricaricato * su * eax immediatamente prima di ogni operazione, ma non viene salvato eax. – Crashworks

+0

Per determinati aritmetici il compilatore _has_ carica una posizione di memoria contrassegnata come volatile in un registro, poiché l'operazione non è possibile come istruzione read-modify-write. Dopo questo segmento di codice potrebbe seguire un negozio e prima potrebbe esserci un carico, quindi dalle informazioni a disposizione può essere assolutamente possibile. Ma posly non è solo una ottimizzazione persa da VC. – hirschhornsalz

6

Questo è stato costruito in modalità di debug? In tal caso, il compilatore memorizza tutte le variabili locali nello stack in modo che il debugger possa trovarle in modo coerente.

L'elisione di tali negozi e ricariche non necessari è una delle ottimizzazioni che costituisce la modalità di "rilascio".

+0

Credo che sia * supposto * essere codice in modalità di rilascio (non esiste una versione "debug" di qualsiasi cosa relativa che possa vedere ovunque ...), ma non sono sicuro ... Non ho il codice sorgente o. Ma +1 è una supposizione ragionevole, grazie. – Mehrdad

2

volatile o no, l'unico tecnica motivo per cui sarebbe EAXdevono essere inizializzato direttamente prima di effettuare una chiamata di funzione su Windows erano se questo function è dichiarato __syscall, vale a dire utilizzando la convenzione di chiamata di Windows CS_SYSCALL. Concettualmente, questo è un po 'simile alla convenzione UN * X x86_64 dove %al contiene il numero di argomenti di tipo floating point passati nei registri %xmm.

La syscall convenzione di chiamata su Windows è identica alla __cdecl, cioè args funzione sul stack in ordine inverso, ma con l'aggiunta che AL contiene un conteggio del numero di argomenti; questo è fatto in modo che il codice del kernel che di solito si trova all'estremità finale di questo sappia quanti dati leggere dallo stack utente sullo stack del kernel per recuperare gli argomenti.

EAX è un registro zero per tutte le convenzioni di chiamata su Windows a 32 bit, il suo valore non viene mai conservato su chiamate di funzione, inizializzandolo direttamente prima di effettuare una chiamata ridondante. Anche se la variabile che contiene era volatile - perché un semplice ricaricamento non è una barriera di memoria e non "commette" un archivio precedente. Inoltre, la posizione [EBP - 4] si trova nello stack , quindi la variabile è locale (e un qualificatore volatile non ha molto senso).

Se non è un'ottimizzazione persa allora potrebbe essere un'invocazione di un __syscall function(...) con un diverso numero di argomenti, come, ipoteticamente,

__syscall printf_syscall_conv(char *fmt, ...); 

void possibly_print_three_vals(char *fmt, int val1, int val2, int val3) 
{ 
    if (*strchr('%', fmt) == '\0') // if no "%" in fmt, pass no args 
     printf_syscall_conv(fmt); 
    else 
     printf_syscall_conv(fmt, val1, val2, val3); 
} 

Questo, concettualmente, potrebbe creare un output di montaggio come la tua.

Problemi correlati