L’obiettivo di questo breve tutorial è quello di fornire alcuni semplici esempi pratici di utilizzo del celebre linguaggio
Assembly. Si presupporrà quindi la conoscenza della teoria che c’è dietro questo linguaggio e, per alcune parti, del linguaggio C. In particolare ci soffermeremo sull’architettura Intel
IA-32 e su un sistema GNU/Linux 2.6.
Per quanto riguarda la sintassi è necessario sottolineare che esistono due differenti dialetti
Assembly molto noti:
- quello relativo agli assemblatori dei sistemi operativi Windows, che prevede una sintassi cosiddetta Intel;
- e quello relativo all’assemblatore GNU as, che prevede la sintassi chiamata AT&T. Questo assemblatore è quello che viene invocato dal compilatore gcc.
Anche se attualmente
as è in grado di gestire entrambe le sintassi, nei nostri esempi utilizzeremo quella
AT&T in quanto attraverso tale sintassi il compilatore
gcc produce i file
Assembly.
Partiamo quindi dal classico "Hello World!" e iniziamo a vedere come il compilatore
gcc stesso costruisce il codice
assembler, ottenuto con l'opzione
–s:
.file "HelloWorld.c"
.section .rodata
.LC0:
.string "Hello World!n"
.text
.globl main
.type main,@function
main:
pushl %ebp
movl %esp, %ebp
subl $8, %esp
andl $-16, %esp
movl $0, %eax
subl %eax, %esp
subl $12, %esp
pushl $.LC0
call printf
addl $16,%esp movl $0, %eax
leave
ret
.size main -main
.ident "GCC: (GNU) 3.3.4"
Come possiamo vedere il programma inizia con una serie di comandi che definiscono il file, i simboli e la stringa "Hello World!". Successivamente troviamo il
main con le prime due istruzioni
pushl e
movl, che rappresentano le classiche istruzioni di
entry al programma
Assembly. Il registro
ebp è un puntatore ai dati dello stack, mentre il registro
esp contiene l'indirizzo della cima dello stack utilizzato dal microprocessore.
Di seguito troviamo due istruzioni
sub, che riservano rispettivamente 8 e 12 byte sullo stack, intervallate da una istruzione di
mov, che azzera l'accumulatore
eax, cioè il valore restituito dal
main. Infine, prima delle istruzioni
leave e
ret che, lasciando nelle giuste condizioni lo stack, concludono la funzione, troviamo il fulcro del codice
Assembly.
Con l'istruzione
pushl $.LCO inseriamo quanto definito in testa al programma come argomento dell'istruzione successiva, la ben nota
printf(). Una volta eseguita la
printf(), infine, vengono rimossi gli argomenti con l'operazione di
add. L'istruzione
add, opposta alla precedente
sub, pulisce lo stack perché le locazioni di memoria con indirizzo lineare minore contengono gli indirizzi aggiunti più di recente.