Co

Condizionali in x86-64 Assembly

4 esercizi

Informazioni su Condizionali

RFLAGs

Esiste un registro speciale chiamato rflags. I suoi bit funzionano come flag per varie condizioni.

Alcuni di essi sono elencati di seguito:

nome simbolo bit
riporto CF 0
zero ZF 6
segno SF 7
overflow OF 11

Per un elenco completo, consulta il manuale di Intel.

Istruzioni di confronto

I flag in rflags non vengono modificati direttamente. Vengono invece impostati da molte istruzioni diverse.

Per esempio, ZF viene impostato da molte operazioni aritmetiche o bit a bit quando il risultato è zero.

Una delle istruzioni più comuni usate per verificare le condizioni è cmp. Prende due operandi e aggiorna i flag, ma non modifica i suoi operandi.

L'istruzione CMP

L'istruzione cmp sottrae il secondo operando dal primo e imposta i flag in base al risultato.

Se A è il primo operando e B il secondo:

flag impostato quando
CF A < B (senza segno)
ZF A == B
SF A < B (con segno, senza overflow)
OF overflow nella sottrazione con segno

Salti

Per impostazione predefinita, il codice in x86-64 viene eseguito in sequenza dall'alto verso il basso.

Ma ci sono molte situazioni in cui è necessario modificare questo comportamento. Per esempio, per eseguire un insieme diverso di istruzioni in risposta a una condizione.

Nei linguaggi di alto livello, questo si fa di solito con astrazioni come i condizionali if...else. Tuttavia, in x86-64 non esistono.

Al loro posto, x86-64 fornisce istruzioni che trasferiscono di fatto l'esecuzione in un'altra posizione del codice. Questo si chiama branching.

Note

Abbiamo già visto due istruzioni di questo tipo: call e ret.

Quando viene chiamata una funzione, l'esecuzione viene trasferita dal chiamante alla funzione chiamata. E, al ritorno, l'esecuzione viene trasferita di nuovo al chiamante.

Se non si trova alcun ret, l'esecuzione passa direttamente da una funzione a quella successiva. Questo a volte può essere usato per ottimizzare il flusso del codice.

Salto incondizionato

L'istruzione jmp trasferisce incondizionatamente l'esecuzione del programma in un altro punto del codice. Il suo unico operando è un'etichetta che contiene l'indirizzo del punto in cui l'esecuzione continuerà.

Considera, per esempio, la seguente funzione:

fn:
    mov rax, 5
    jmp end

    add rax, 10
end:
    ret

Quando viene chiamata fn, l'esecuzione inizia da mov rax, 5. Questo imposta il valore di rax a 5 in quel punto.

L'istruzione successiva è jmp end, che trasferisce l'esecuzione all'etichetta end.

Dopo end, l'istruzione successiva è ret, che trasferisce l'esecuzione di nuovo alla funzione chiamante.

Nota che, dato che add rax, 10 si trova dopo jmp end e prima di end, non viene mai eseguita. Quando fn termina, il valore di rax è 5.

Salto condizionato

La famiglia di istruzioni jcc trasferisce l'esecuzione del programma in un altro punto solo se una condizione specifica è soddisfatta. Altrimenti, l'esecuzione continua in sequenza.

Ogni condizione corrisponde a uno o più flag in rflags. Alcune varianti di jcc verificano che un flag sia impostato, altre che sia azzerato.

Il cc in jcc non è letterale, ma si riferisce al suffisso specifico associato al flag verificato.

Ci sono molti suffissi e molti di essi verificano la stessa condizione di un altro. Alcuni di essi si riferiscono direttamente a un flag, così che l'istruzione salta a un'etichetta se il flag specifico è impostato:

suffisso salta se
z ZF == 1
c CF == 1
s SF == 1
o OF == 1

Molti altri sono scelti per riferirsi al loro significato in un'istruzione cmp. Per esempio:

istruzione suffisso salta se
cmp A, B e A == B
cmp A, B l A < B (con segno)
cmp A, B b A < B (senza segno)
cmp A, B g A > B (con segno)
cmp A, B a A > B (senza segno)

È possibile aggiungere e dopo l, b, g o a per includere l'uguaglianza nella condizione:

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

Per tutti i suffissi, esistono varianti che verificano il comportamento opposto. Hanno la stessa sintassi, ma con una n prima del suffisso.

Per esempio, jnz salta quando ZF non è impostato. Analogamente, jnae salta quando A non è >= B (A e B interpretati come numeri interi senza segno).

Note

Alcuni suffissi sono alias delle stesse condizioni. Per esempio, jz (suffisso z, per ZF) e je (suffisso e, per «uguale») saltano entrambi quando ZF è impostato. Questo perché, con cmp, ZF viene impostato quando la sottrazione dà zero, il che corrisponde al fatto che i due operandi sono uguali.

Altri suffissi, invece, verificano una combinazione di flag e non possono essere sostituiti direttamente da un singolo suffisso di flag.

Preferisci il suffisso che descrive meglio la semantica del tuo confronto.

Etichette locali

Le etichette sono visibili nell'intero file sorgente, non sono locali a una funzione. Quindi è impossibile riutilizzare il nome di un'etichetta.

Per imitare il comportamento di un'etichetta locale, NASM ha una notazione speciale per un'etichetta dichiarata con un punto (.) davanti. Questa notazione definisce un'etichetta che include implicitamente il nome dell'etichetta precedente non puntata:

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

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

È comunque possibile saltare a questa etichetta da qualsiasi punto del codice usando il nome completo dell'etichetta, per esempio jmp fn1.example.

Tuttavia, un salto che usa la parte dell'etichetta a partire dal punto viene effettuato verso l'etichetta all'interno della funzione superiore. Per esempio, .example si comporta come se fosse locale alla funzione:

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

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

Nota che un'etichetta non puntata all'interno di una funzione, in pratica, definisce un'altra funzione:

section .text
fn1:
    ...
.example: ; this is 'fn1.example'
    ...
non_dotted:
    ...
.example: ; this is 'non_dotted.example'
    ...
    ret
Modifica tramite GitHub Il collegamento si apre in una nuova finestra o scheda

Impara Condizionali