Σκοπός
Ο στόχος της άσκησης αυτής είναι να βοηθήσει στην εισαγωγή (1) σε assembly των επεξεργαστών της αρχιτεκτονικής x86, (2) στην αναπαράσταση της γλώσσας assembly σε κώδικα μηχανής στη μνήμη του συστήματος, και (3) στην χρήση του gdb.
Background
Η άσκηση αυτή θα γίνει στα μηχανήματα, αρχιτεκτονικής x86, του τμήματος με λειτουργικό σύστημα GNU/Linux (portokali, milo, rodakino, etc.). Για θέματα της αρχιτεκτονικής του x86 και της γλώσσας assembly που χρησιμοποιεί μπορείτε να συμβουλευτείτε την online έκδοση του βιβλίου: Programming from the Ground Up, Jonathan Bartlett 2004. (Τοπικό αντίγραφο του βιβλίου για ευκολία). Ιδιαίτερα χρήσιμα θα βρείτε τα Chapter 3 και Appendix B. Ακόμη για αντιστοίχηση εντολών σε opcode μπορείτε να συμβουλευτείτε αυτόν τον πίνακα (προσοχή τα ονόματα κάποιον εντολών διαφέρουν λίγο, π.χ. RET/RETN)
Η άσκηση
1. Δίνεται το πρόγραμμα x86asm.s.
(a) Κάντε compile και εκτελέστε το πρόγραμμα με την εντολή:
gcc -o x86asm x86asm.sΤι τυπώνει το πρόγραμμα; Δώστε την απάντησή σας στο readme αρχείο της άσκησης σας.
(b) Αντιγράψτε το αρχείο σε ένα x86asm_comments.s αρχείο και συμπληρώστε σχόλια που δείχνουν τι κάνει το πρόγραμμα σε κάθε βήμα του. Είναι αρκετό να συμπληρώσετε σύντομα σχόλια δίπλα σε κάθε σύμβολο "#" που δεν περιέχει ήδη κάποιο σχόλιο.
(c) Εκτελέστε το πρόγραμμα σας μέσα από τον gdb.
Κάντε compile το x86asm.s με την εντολή
gcc -g -o x86asm x86asm.sΚαλέστε τον gdb και εκτελέστε το πρόγραμμα x86asm.
$ gdb x86asm (gdb) run
(d) Εξετάστε το πρόγραμμα σας μέσα από τον gdb.
Χρησιμοποιήστε την εντολή list "l" (help list) για να δείτε τον source κώδικα του προγράμματος από το source αρχείο x86asm.s.
(gdb) l mainΧρησιμοποιήστε την εντολή examine "x" (help examine) για να δείτε τα περιεχόμενα της μνήμης του προγράμματος ως εντολές assembly.
(gdb) x/??i *mainόπου ?? είναι ο κατάλληλος αριθμός εντολών. Συμπληρώστε στο readme αρχείο σας ποιος είναι ο αριθμός εντολών της main.
Χρησιμοποιήστε την εντολή examine "x" (help examine) για να δείτε τα περιεχόμενα της μνήμης του προγράμματος ως bytes.
(gdb) x/??b *mainόπου ?? είναι ο κατάλληλος αριθμός bytes. Συμπληρώστε στο readme αρχείο σας ποιος είναι ο αριθμός bytes που καταλαμβάνουν οι εντολές της main.
Βάλτε ένα breakpoint μετά το τέλος του loop στη main:
(gdb) b *main+?? # (όπου ?? το κατάλληλο offset) (gdb) runΕκτελέστε την υπόλοιπη συνάρτηση main (μέχρι το τελικό μήνυμα του gdb "Program exited normally.", με διαδοχικές εντολές "ni". (Πατώντας enter στο gdb shell επανεκτελείται η τελευταία εντολή)
(gdb) ni ... (gdb) niΣυμπληρώστε στο readme αρχείο σας το offset που χρησιμοποιήσατε.
(e) Εκτελέστε το πρόγραμμα πάλι και σταματήστε στο προηγούμενο breakpoint. Εξετάστε τα περιεχόμενα των θέσεων μνήμης S, Msg με την εντολή print "p".
(gdb) p (int )S (gdb) p (int *)&S (gdb) p (char *)&MsgΓράψτε στο readme file σας τις τιμές τους και τις διευθύνσεις στις οποίες βρίσκονται.
Αλλάξτε την τιμή της διεύθυνσης S σε 99:
(gdb) set *(int *)&S = 99Εκτελέστε το πρόγραμμα μέχρι το τέλος με διαδοχικές εντολές "ni". Συμπληρώστε στο readme αρχείο το μήνυμα που τυπώνει το πρόγραμμα.
2. Δίνεται το πρόγραμμα sum.c.
(a) Κάντε compile και εκτελέστε το πρόγραμμα με την εντολή:
gcc -Wall -pedantic -ansi -o sum sum.c ./sumΤι τυπώνει το πρόγραμμα; Δώστε την απάντησή σας στο readme αρχείο της άσκησης σας.
(b) Μεταφράστε το πρόγραμμα σε assembly με τον gcc με την εντολή.
gcc -Wall -pedantic -ansi -O2 -S sum.cΕξετάστε στο αρχείο sum.s που παράγεται και συμπληρώστε στο readme αρχείο σας από πόσες εντολές assembly αποτελείται η συνάρτηση main.
(c) Εκτελέστε και εξετάστε το πρόγραμμα μέσα από τον gdb.
Κάντε compile το πρόγραμμα με την εντολή:
gcc -Wall -pedantic -ansi -g -O2 -o sum sum.cΚαλέστε τον gdb και εκτελέστε το πρόγραμμα sum.
$ gdb sum (gdb) run
(d) Χρησιμοποιήστε την εντολή list "l" (help list) για να δείτε τον source κώδικα του προγράμματος από το source αρχείο sum.c.
(gdb) l mainΧρησιμοποιήστε την εντολή examine "x" (help examine) για να δείτε τα περιεχόμενα της μνήμης του προγράμματος ως εντολές assembly.
(gdb) x/??i *mainόπου ?? είναι ο κατάλληλος αριθμός εντολών. Συμπληρώστε στο readme αρχείο σας ποιος είναι ο αριθμός εντολών της main.
Χρησιμοποιήστε την εντολή examine "x" (help examine) για να δείτε τα περιεχόμενα της μνήμης του προγράμματος ως bytes.
(gdb) x/??b *mainόπου ?? είναι ο κατάλληλος αριθμός bytes. Συμπληρώστε στο readme αρχείο σας ποιος είναι ο αριθμός bytes που καταλαμβάνουν οι εντολές της main.
Βάλτε ένα breakpoint μετά το τέλος του loop στη main:
(gdb) b ?? # (όπου ?? το κατάλληλο offset στο source αρχείο) (gdb) runΕκτελέστε την υπόλοιπη συνάρτηση main (μέχρι το τελικό μήνυμα του gdb "Program exited normally." με διαδοχικές εντολές "n". (Πατώντας enter στο gdb shell επανεκτελείται η τελευταία εντολή)
(gdb) n ... (gdb) nΣυμπληρώστε στο readme file σας το offset που χρησιμοποιήσατε για το breakpoint.
(e) Εκτελέστε το πρόγραμμα πάλι και σταματήστε στο προηγούμενο breakpoint. Εξετάστε τα περιεχόμενα των μεταβλητών Sum, n.
(gdb) p Sum (gdb) p &Sum (gdb) p n (gdb) p &nΓράψτε στο readme file σας τις τιμές τους και τις διευθύνσεις στις οποίες βρίσκονται.
Αλλάξτε την τιμή της μεταβλητής Sum σε 98 και 99 διαδοχικά με δύο διαφορετικούς τρόπους:
(gdb) set Sum = 98 (gdb) set *(int *)?? = 99 # (όπου ?? η διεύθυνση της μεταβλητής Sum στη μνήμη)Εκτελέστε το πρόγραμμα μέχρι το τέλος με διαδοχικές εντολές "n". Συμπληρώστε στο readme file το μήνυμα που τυπώνει το πρόγραμμα.
(f) Εκτελέστε το πρόγραμμα πάλι και σταματήστε με ένα breakpoint στην αρχή της main. Αλλάξτε την εντολή cmp στη μνήμη ώστε να συγκρίνει τη μεταβλητή "n" με την τιμή 100 αντί για την τιμή 0. Μπορείτε να χρησιμοποιήσετε μια ακολουθία εντολών όπως η παρακάτω:
(gdb) x/??i *main # (όπου ?? ο αριθμός εντολών assembly στη main) (gdb) x/2i ?? # (όπου ?? η διεύθυνση της εντολής cmp) (gdb) x/??b ???? # (όπου ?? ο αριθμός bytes που καταλαμβάνει η εντολή cmp και ???? η διεύθυνση της) (gdb) set *(char *)(?? + ????) = 100 # (όπου ?? η διεύθυνση της εντολής cmp όπως παραπάνω και ???? το offset του byte της εντολής cmp που περιέχει την τιμή 0) (gdb) x/2i ?? # (όπου ?? η διεύθυνση της εντολής cmp, θα δείτε την εντολή cmp να έχει διαφορετικά operands)Σβήστε το breakpoint και εκτελέστε το πρόγραμμα μέχρι το τέλος:
(gdb) d (gdb) cΣυμπληρώστε στο readme file σας το μήνυμα που τυπώνει το πρόγραμμα.
3. Μας ζητούν να αντικαταστήσουμε στο πρόγραμμα sum.c το while loop με πιο "αποτελεσματικό" κώδικα assembly. Οπότε χρησιμοποιούμε τον κώδικα του x86asm.s. Δίνεται το αρχείο x86sum.c που περιέχει "inline assembly" κώδικα μέσα στο C πρόγραμμα.
(a) Κάντε compile το πρόγραμμα x86sum.c με την εντολή
gcc -Wall -pedantic -ansi -fasm -o x86sum x86sum.cΕκτελέστε το πρόγραμμα και συμπληρώστε στο readme file σας το μήνυμα που τυπώνει.
(b) Κάντε compile το πρόγραμμα x86sum.c με την εντολή
gcc -Wall -pedantic -ansi -fasm -S x86sum.cΜετρήστε και συμπληρώστε στο readme file σας τον αριθμό των εντολών assembly που υπάρχουν στην main.
(c) Αλλάξτε το όνομα των μεταβλητών "n, Sum" σε "i, S" αντίστοιχα και ονομάστε το νέο source file x86sum2.c. Κάντε compile το νέο πρόγραμμα όπως παραπάνω, εκτελέστε το, και συμπληρώστε στο readme file το μήνυμα που τυπώνει.
Logistics
- Χρησιμοποιήστε τα μηχανήματα x86 με λειτουργικό σύστημα GNU/Linux του τμήματος και τα εργαλεία gcc, emacs/vim/nano, gdb.
- Δημιουργήστε ένα "readme" text file που περιέχει:
- Το όνομά σας
- Πράγματα που χειρίζεστε με διαφορετικό τρόπο από ότι ορίζει η άσκηση.
- Μια περιγραφή της βοήθειας που είχατε από άλλους στη δημιουργία του προγράμματος σας, και σε συμφωνία με το Policies section του web page του μαθήματος.
- (Προαιρετικά) Μία ένδειξη του πόσο χρόνο αφιερώσατε για την άσκηση.
- (Προαιρετικά) Οτιδήποτε άλλο θέλετε να αναφέρετε.
- Παραδώστε τα αρχεία της άσκησης: x86asm_comments.s, x86sum2.c, readme.
Turnin
Υποβάλετε την άσκηση σας όπως αναφέρεται στο section Policies του web page του μαθήματος.
Βαθμολογία
Η βαθμολογία θα βασιστεί στην κατανόηση της άσκησης.