Il processo di compilazione
Un compilatore è un programma che traduce il codice sorgente scritto in un linguaggio di programmazione ad alto livello, come C++ o Java, in un linguaggio macchina comprensibile ed eseguibile dalla CPU. Il risultato finale è un file binario eseguibile, che può essere eseguito direttamente dalla CPU senza la necessità del codice sorgente originale.
Prendiamo per esempio un semplice programma scritto in C.
#include <stdio.h>
int main (){
puts("Hello World!");
return 0;
}
Possiamo compilarlo con un compilatore, e possiamo ispezionare il codice assembly generato tramite degli strumenti come objdump
.
$ objdump -M intel -d main
[...]
0000000000001139 <main>:
1139: 55 push rbp
113a: 48 89 e5 mov rbp,rsp
113d: 48 8d 05 c0 0e 00 00 lea rax,[rip+0xec0] # 2004 <_IO_stdin_used+0x4>
1144: 48 89 c7 mov rdi,rax
1147: e8 e4 fe ff ff call 1030 <puts@plt>
114c: b8 00 00 00 00 mov eax,0x0
1151: 5d pop rbp
1152: c3 ret
Possiamo chiaramente identificare l'istruzione che chiama la funzione puts
, ovvero
call 1030 <puts@plt>
e le istruzioni che fanno ritornare 0 dalla funzione (il valore di ritorno di una funzione in x86 è messo nel registro rax
)
mov eax,0x0
pop rbp
ret
Il processo di traduzione che porta dal codice sorgente al programma eseguibile attraversa varie fasi di compilazione e ottimizzazione. Ad esempio un compilatore per il linguaggio C deve prima eseguire il preprocessore, che risolve tutte le direttive ottenendo il codice sorgente completo. Questo poi passa al compilatore vero e proprio, che trasforma un file sorgente (più nello specifico una translation unit) in codice assembly. Infine l'assemblatore trasforma il file assembly in un file oggetto.
Questo però non è ancora eseguibile, perché potrebbe avere delle dipendenze con librerie o con altri file oggetto. Queste dipendenze sono risolte dal linker, che combina tutti i file oggetto in un unico file eseguibile. Il processo di linking può essere statico, ovvero le librerie sono incorporate all'interno del binario finale, oppure dinamico, ovvero vengono lasciate delle reference alle librerie necessarie, che saranno poi caricate a tempo di esecuzione.