Indietro:
introduzione
Strumenti di Sviluppo (compilatori, assemblatori, linker)
Gli strumenti di sviluppo sono programmi che convertono un programma
da un livello di astrazione incentrato sulle persone,
ad un livello di astrazione centrato sulla macchina.
Per molti programmatori, l'interfaccia principale
con la macchina è l'ambiente di compilazione.
L'ambiente di compilazione prende come input uno o più
file in un linguaggio di alto livello come il C o Java,
più una serie di file di supporto come file risorse
e librerie, e converte il tutto in un file eseguibile
per uno specifico ambiente di esecuzione, per esempio
Linux o Windows (non consideriamo rappresentazioni in
linguaggi a livello ancora più elevato come linguaggi
di modellazione UML o linguaggi per domini specifici,
sebbene anche loro possano essere oggetto di decompilazione).
La compilazione avviene tramine un certo numero di passi
che coinvolgono strumenti specifici:
- Ogni linguaggio ad alto livello viene compilato
in un file in formato assembler da un compilatore
per quel linguaggio specifico.
- Ogni file in formato assembler, che sia stato
creato da un compilatore o direttamente dal programmatore,
è convertito in un file oggetto rilocabile
da un programma assemblatore. L'assemblatore non è
interessato a quale fosse il linguaggio ad alto livello originale.
L'unica cosa di cui si preoccupa è di quale processore
eseguirà il codice, in modo da convertire le linee
testo in sequenze di byte che possano essere eseguite dal processore.
Questo è il primo stadio in cui vengono perse delle
informazioni, poichè l'assemblatore può ignorare molte
delle informazioni che possono essere utili al programmatore,
come variabili locali e i loro tipi di dati.
- Ogni file oggetto rilocabile è combinato insieme ad un
numero di librerie che forniscono il supporto all'ambiente di esecuzione
da un programma chiamato "linker". Al linker può non importare
quale processore andrà a eseguire il programma. In teoria
tutto quello che interessa al linker è come formattare il
programma in modo che possa essere caricato in memoria ed eseguito
dal sistema operativo. Perciò il linker può decidere
di rimuovere informazioni dal file binario generato quando pensa che
tali informazioni non sono necessarie all'esecuzione del programma.
Come si vede, ad ogni stadio si perdono delle informazioni che erano
vitali al programmatore quando ha scritto il programma, ma che non
sono necessarie quando il programma viene eseguito dalla macchina.
Ancora peggio, lo stesso programmatore può istruire gli strumenti
di sviluppo ad aggiungere o rimuovere informazioni utili. Quando si usa un compilatore,
l'utente può decidere di:
- generare informazioni aggiuntive per migliorare la debuggabilità
del codice (l'opzione -g nei compilatori Unix è di solito usata
per questo scopo).
- generare codice che è più difficile da capire per gli umani,
ma è eseguito più efficientemente dai processori; cioè
generare codice ottimizzato tramite la specifica del livello di ottimizzazione
tramite le opzioni -O1, -O2 ecc. I compilatori ottimizzanti, tramite un
numero di trasformazioni che eseguono sulle istruzioni generate, rendono
il codice finale meno leggibile anche quando le informazioni di debugging
sono presenti nel file finale, ed il file sorgente rimane disponibile
per l'ispezione tramite un debugger. Il debugging di codice ottimizzato
è un'area di ricerca di interesse a se, e non sarà considerata
in questo documento, sebbene molte delle tecniche possono essere applicate
a un "debugger de-ottimizzatore".
Anche quando si usa uno degli altri strumenti l'utente può influire
sull'esecuzione di un decompilatore, per esempio dicendo al linker di
rimuovere tutte le informazioni simboliche dal file binario.
Da questo punto in avanti, ogni strumento che possiamo usare per analizzare
il programma può essere considerato uno strumento di ingenieria inversa.
Avanti: Strumenti di Ingenieria Inversa