Συ

Συνθήκες σε x86-64 Assembly

4 ασκήσεις

Σχετικά με την έννοια Συνθήκες

RFLAGs

Υπάρχει ένας ειδικός καταχωρητής που ονομάζεται rflags. Τα bit του λειτουργούν σαν σημαίες για διάφορες συνθήκες.

Μερικές από αυτές είναι οι παρακάτω:

όνομα σύμβολο bit
κρατούμενο CF 0
μηδέν ZF 6
πρόσημο SF 7
υπερχείλιση OF 11

Για την πλήρη λίστα, ανατρέξε στο Εγχειρίδιο της Intel.

Εντολές σύγκρισης

Οι σημαίες στον rflags δεν τροποποιούνται άμεσα. Αντ' αυτού, ορίζονται από πολλές διαφορετικές εντολές.

Για παράδειγμα, η ZF ορίζεται από πολλές αριθμητικές πράξεις ή πράξεις σε επίπεδο bit, όταν το αποτέλεσμα είναι μηδέν.

Μία από τις πιο συνηθισμένες εντολές που χρησιμοποιούνται για τον έλεγχο συνθηκών είναι η cmp. Δέχεται δύο τελεστέους και ενημερώνει τις σημαίες, αλλά δεν τροποποιεί τους τελεστέους της.

Η εντολή CMP

Η εντολή cmp αφαιρεί τον δεύτερο τελεστέο από τον πρώτο και ορίζει τις σημαίες ανάλογα με το αποτέλεσμα.

Αν το A είναι ο πρώτος τελεστέος και το B ο δεύτερος:

σημαία ορίζεται όταν
CF A < B (χωρίς πρόσημο)
ZF A == B
SF A < B (με πρόσημο, χωρίς υπερχείλιση)
OF υπερχείλιση σε αφαίρεση με πρόσημο

Διακλάδωση

Από προεπιλογή, ο κώδικας σε x86-64 εκτελείται διαδοχικά από πάνω προς τα κάτω.

Υπάρχουν όμως πολλές περιπτώσεις όπου είναι απαραίτητο να τροποποιηθεί αυτή η συμπεριφορά. Για παράδειγμα, για να εκτελεστεί ένα διαφορετικό σύνολο εντολών ως απάντηση σε μια συνθήκη.

Σε γλώσσες υψηλότερου επιπέδου, αυτό γίνεται συνήθως με αφαιρέσεις όπως οι συνθήκες if...else. Ωστόσο, αυτές δεν υπάρχουν σε x86-64.

Αντ' αυτού, το x86-64 παρέχει εντολές που μεταφέρουν αποτελεσματικά την εκτέλεση σε άλλο σημείο του κώδικα. Αυτό ονομάζεται branching.

Note

Έχουμε ήδη δει δύο τέτοιες εντολές: τις call και ret.

Όταν καλείται μια συνάρτηση, η εκτέλεση μεταφέρεται από αυτόν που καλεί στη συνάρτηση που καλείται. Και, κατά την επιστροφή, η εκτέλεση μεταφέρεται πίσω σε αυτόν που κάλεσε.

Αν δεν βρεθεί ret, η εκτέλεση συνεχίζει από τη μία συνάρτηση στην επόμενη. Αυτό μπορεί μερικές φορές να χρησιμοποιηθεί για τη βελτιστοποίηση της ροής του κώδικα.

Άλμα χωρίς συνθήκη

Η εντολή jmp μεταφέρει την εκτέλεση του προγράμματος χωρίς συνθήκη σε άλλο σημείο του κώδικα. Ο μοναδικός της τελεστέος είναι μια ετικέτα που περιέχει τη διεύθυνση του σημείου όπου θα συνεχιστεί η εκτέλεση.

Ας δούμε, για παράδειγμα, την παρακάτω συνάρτηση:

fn:
    mov rax, 5
    jmp end

    add rax, 10
end:
    ret

Όταν καλείται η fn, η εκτέλεση ξεκινά από το mov rax, 5. Αυτό ορίζει την τιμή του rax σε 5 σε εκείνο το σημείο.

Η επόμενη εντολή είναι η jmp end, η οποία μεταφέρει την εκτέλεση στην ετικέτα end.

Μετά το end, η επόμενη εντολή είναι η ret, η οποία μεταφέρει την εκτέλεση πίσω στη συνάρτηση που κάλεσε.

Παρατήρησε ότι, επειδή το add rax, 10 βρίσκεται μετά το jmp end και πριν από το end, δεν εκτελείται ποτέ. Η τιμή του rax όταν επιστρέφει η fn είναι 5.

Άλμα υπό συνθήκη

Η οικογένεια εντολών jcc μεταφέρει την εκτέλεση του προγράμματος σε άλλο σημείο μόνο αν ικανοποιείται μια συγκεκριμένη συνθήκη. Διαφορετικά, η εκτέλεση συνεχίζεται διαδοχικά.

Κάθε συνθήκη αντιστοιχεί σε μία ή περισσότερες σημαίες στον rflags. Κάποιες παραλλαγές της jcc ελέγχουν ότι μια σημαία είναι ορισμένη, άλλες ελέγχουν ότι είναι μηδενισμένη.

Το cc στο jcc δεν είναι κυριολεκτικό, αλλά αναφέρεται στο συγκεκριμένο επίθημα που σχετίζεται με τη σημαία που ελέγχεται.

Υπάρχουν πολλά επιθήματα και πολλά από αυτά ελέγχουν την ίδια συνθήκη με κάποιο άλλο. Κάποια από αυτά αναφέρονται απευθείας σε μια σημαία, ώστε η εντολή να κάνει άλμα σε μια ετικέτα αν η συγκεκριμένη σημαία είναι ορισμένη:

επίθημα κάνει άλμα αν
z ZF == 1
c CF == 1
s SF == 1
o OF == 1

Πολλά άλλα επιλέγονται ώστε να παραπέμπουν στη σημασία τους σε μια εντολή cmp. Για παράδειγμα:

εντολή επίθημα κάνει άλμα αν
cmp A, B e A == B
cmp A, B l A < B (με πρόσημο)
cmp A, B b A < B (χωρίς πρόσημο)
cmp A, B g A > B (με πρόσημο)
cmp A, B a A > B (χωρίς πρόσημο)

Είναι δυνατό να προσθέσεις το e μετά το l, το b, το g ή το a για να συμπεριλάβεις την ισότητα στη συνθήκη:

cmp rcx, r8
jge two      ; this jumps to 'two' if rcx is greater than, or equal to, r8 in a signed comparison
jbe two      ; this jumps to 'two' if rcx is lesser than, or equal to, r8 in an unsigned comparison

Για όλα τα επιθήματα, υπάρχουν παραλλαγές που ελέγχουν την αντίθετη συμπεριφορά. Έχουν την ίδια σύνταξη, αλλά με ένα n πριν από το επίθημα.

Για παράδειγμα, η jnz κάνει άλμα όταν η ZF δεν είναι ορισμένη. Παρομοίως, η jnae κάνει άλμα όταν το A δεν είναι >= το B (τα A και B ερμηνεύονται ως ακέραιοι χωρίς πρόσημο).

Note

Κάποια επιθήματα είναι ψευδώνυμα για τις ίδιες συνθήκες. Για παράδειγμα, οι jz (επίθημα z, για το ZF) και je (επίθημα e, για την ισότητα) κάνουν και οι δύο άλμα όταν η ZF είναι ορισμένη. Αυτό συμβαίνει επειδή, με την cmp, η ZF ορίζεται όταν η αφαίρεση δίνει μηδέν, το οποίο αντιστοιχεί στο ότι οι δύο τελεστέοι είναι ίσοι.

Άλλα επιθήματα, ωστόσο, ελέγχουν συνδυασμό σημαιών και δεν μπορούν να αντικατασταθούν άμεσα από ένα επίθημα μιας μόνο σημαίας.

Προτίμησε το επίθημα που περιγράφει καλύτερα τη σημασιολογία της σύγκρισής σου.

Τοπικές ετικέτες

Οι ετικέτες είναι ορατές σε ολόκληρο το αρχείο πηγαίου κώδικα, δεν είναι τοπικές σε μια συνάρτηση. Έτσι, είναι αδύνατο να ξαναχρησιμοποιήσεις ένα όνομα ετικέτας.

Για να μιμηθεί τη συμπεριφορά μιας τοπικής ετικέτας, το NASM έχει έναν ειδικό συμβολισμό για μια ετικέτα που δηλώνεται με τελεία (.) πριν από αυτήν. Αυτός ο συμβολισμός ορίζει μια ετικέτα που περιλαμβάνει σιωπηρά το όνομα της προηγούμενης ετικέτας χωρίς τελεία:

section .text
fn1:
    ...
.example: ; this is fn1.example
    ...
    ret

fn2:
    ...
.example: ; this is fn2.example
    ...
    ret

Είναι ακόμα δυνατό να κάνεις άλμα σε αυτήν την ετικέτα από οποιοδήποτε σημείο του κώδικα χρησιμοποιώντας το πλήρες όνομα της ετικέτας, για παράδειγμα, jmp fn1.example.

Ωστόσο, ένα άλμα που χρησιμοποιεί το τμήμα της ετικέτας που ξεκινά από την τελεία θα γίνει στην ετικέτα μέσα στην εξωτερική συνάρτηση. Για παράδειγμα, το .example συμπεριφέρεται σαν να ήταν τοπικό στη συνάρτηση:

section .text
fn1:
    ...
.example:
    ...
    jmp .example ; this jumps to fn1.example

fn2:
    ...
.example:
    ...
    jmp .example ; this jumps to fn2.example

Σημείωσε ότι μια ετικέτα χωρίς τελεία μέσα σε μια συνάρτηση στην πράξη ορίζει μια άλλη συνάρτηση:

section .text
fn1:
    ...
.example: ; this is 'fn1.example'
    ...
non_dotted:
    ...
.example: ; this is 'non_dotted.example'
    ...
    ret
Επεξεργασία μέσω GitHub Ο σύνδεσμος ανοίγει σε νέο παράθυρο ή καρτέλα

Μάθε την έννοια Συνθήκες