Esercizi sull'ISA IA32
Nota: se
gcc con l'opzione
-m32 su Linux a 64 bit non riesce a trovare delle header e termina con un errore, probabilmente non sono installate le librerie C a 32 bit. In distribuzioni Linux che usano
apt come package manager, provare con il comando:
sudo apt-get install libc6-dev-i386.
Esercizio 1
Si traduca in C il seguente programma IA32:
e1:
pushl %ebp
movl %esp, %ebp
movl 8(%ebp), %eax
imull %eax, %eax
addl 12(%ebp), %eax
popl %ebp
ret
Si risponda inoltre alle seguenti domande:
- Cosa calcola la funzione e1?
- Cosa sarebbe cambiato se invece di eax avessimo usato ecx?
Esercizio 2
Si traduca in C il seguente programma IA32:
e2:
pushl %ebp
movl %esp, %ebp
movw 8(%ebp), %ax
movw 12(%ebp), %cx
cmpw %ax, %cx
cmovgew %cx, %ax
movswl %ax, %eax
popl %ebp
ret
Si risponda inoltre alle seguenti domande:
- Cosa calcola la funzione e2?
- Come verrebbe compilato il programma se gcc fosse invocato con l'opzione -fomit-frame-pointer (che non genera prologo ed epilogo che gestisce lo stack frame usando ebp)?
- Come avremmo dovuto modificare il programma se invece di cx avessimo usato bx?
Esercizio 3
Si traduca in C il seguente programma IA32:
e3:
pushl %edi
pushl %esi
xorl %eax, %eax
movl 20(%esp), %ecx
movl 16(%esp), %edx
movl 12(%esp), %esi
jmp L4
L2:
xorl %eax, %eax
jmp L6
L1:
movl (%edx,%eax,4), %edi
cmpl %edi, (%esi,%eax,4)
jne L2
incl %eax
L4:
cmpl %ecx, %eax
jl L1
movl $1, %eax
L6:
popl %esi
popl %edi
ret
Si risponda inoltre alle seguenti domande:
- Cosa calcola la funzione e3?
- Cosa succederebbe se si eliminassero dal codice le operazioni di push e pop?
Esercizio 4
Si traduca in C il seguente programma IA32:
e4:
pushl %ebp
movl %esp, %ebp
pushl %esi
movl 8(%ebp), %eax
movw (%eax), %cx
movl 12(%ebp), %edx
movw (%edx), %si
movw %si, (%eax)
movw %cx, (%edx)
popl %esi
popl %ebp
ret
Si risponda inoltre alla seguente domanda:
- Cosa calcola la funzione e4?
Esercizio 5
Si traduca in C il seguente programma IA32:
e5:
movl 4(%esp), %ecx
movl 8(%esp), %edx
pushl (%ecx)
pushl (%edx)
popl (%ecx)
popl (%edx)
ret
Si risponda inoltre alla seguente domanda:
- Cosa calcola la funzione e5?
Esercizio 6
Si consideri la seguente funzione:
int e6(char x) {
if (x>0) return 1;
else return 0;
}
e la sua traduzione in assembly x86 IA32 usando
gcc senza ottimizzazioni (livello di ottimizzazione
-O0):
e6:
subl $12, %esp
movb 16(%esp), %al
movb %al, 11(%esp)
movb 11(%esp), %al
cmpb $0, %al
jle L2
movl $1, (%esp)
jmp L3
L2:
movl $0, (%esp)
L3:
movl (%esp), %eax
movl %eax, 4(%esp)
movl 4(%esp), %eax
addl $12, %esp
ret
Si ottenga una versione più compatta del codice assembly che usa il minor numero possibile di istruzioni.
Esercizio 7
Il seguente frammento di programma assembly IA32 è errato. Spiegare perché e correggere l'errore:
e7:
pushl $10
call f
ret
Esercizio 8
Si completi l'intestazione della funzione
e8, che calcola
e8(x,y)=|x-y|:
int e8(___________) {
if (x>y) return x-y;
return y-x;
}
analizzando la sua traduzione in assembly IA32:
e8:
pushl %ebp
movl %esp, %ebp
movw 12(%ebp), %cx
movw 8(%ebp), %dx
cmpw %cx, %dx
jle L3
movswl %dx, %eax
movswl %cx, %ecx
L2:
subl %ecx, %eax
popl %ebp
ret
L3:
movswl %cx, %eax
movswl %dx, %ecx
jmp L2
Esercizio 9
Scaricare i seguenti due file:
.globl e9
e9:
pushl %ebp
movl %esp, %ebp
movsbl 8(%ebp), %eax
movsbl 12(%ebp), %ecx
subl %ecx, %eax
popl %ebp
ret
#include <stdio.h>
int e9
(char x,
char y
);
int main
() {
int d = e9
(10,
7);
printf("%d\n", d
);
return 0;
}
e generare un file eseguibile chiamato
e9 compilandoli:
- con livello di ottimizzazione 1
- per piattaforma a 32 bit
Dopo aver testato il funzionamento del programma eseguendolo (il programma dovrebbe stampare 3 sul terminale), modificare il file
e9.s in modo che il registro
ebp non compaia più.
- Nota: su Mac OS X rimpiazzare e9 con _e9 nel file e9.s.
Esercizio 10
Scaricare i seguenti due file, contenenti una funzione
e10 scritta in assembly IA32 che (dovrebbe) calcolare il massimo di due
int e un
main di prova:
.globl e10
e10:
pushl %ebp
movl 8(%ebp), %eax
movl 4(%ebp), %ecx
cmpl %eax, %ecx
cmoval %ecx, %eax
popl %ebp
ret
#include <stdio.h>
int e10
(int x,
int y
);
// calcola il massimo tra x e y
int main
() {
printf("%d [atteso=23]\n", e10
(23,
17));
printf("%d [atteso=17]\n", e10
(-23,
17));
printf("%d [atteso=41]\n", e10
(19,
41));
printf("%d [atteso=-5]\n", e10
(-5,
-13));
printf("%d [atteso=6]\n", e10
(6,
-6));
printf("%d [atteso=1]\n", e10
(1,
1));
return 0;
}
e generare un file eseguibile chiamato
e10 compilandoli:
- con livello di ottimizzazione 1
- per piattaforma a 32 bit
- Nota: su Mac OS X rimpiazzare e10 con _e10 nel file e10.s.
Il programma stamperà un risultato errato, ad esempio:
$ ./e10
-1787013415 [atteso=23]
-1787013415 [atteso=17]
-1787013415 [atteso=41]
-178701