Co

Conditionnelles en x86-64 Assembly

4 exercices

À propos de Conditionnelles

Les RFLAGs

Il existe un registre particulier appelé rflags. Ses bits agissent comme des drapeaux pour différentes conditions.

En voici quelques-uns :

nom symbole bit
retenue CF 0
zéro ZF 6
signe SF 7
débordement OF 11

Pour une liste complète, consulte le manuel d'Intel.

Les instructions de comparaison

Les drapeaux de rflags ne sont pas modifiés directement. Ils sont en revanche positionnés par de nombreuses instructions différentes.

Par exemple, ZF est positionné par de nombreuses opérations arithmétiques ou bit à bit lorsque le résultat vaut zéro.

L'une des instructions les plus courantes pour tester des conditions est cmp. Elle prend deux opérandes et met à jour les drapeaux, mais ne modifie pas ses opérandes.

L'instruction CMP

L'instruction cmp soustrait le second opérande du premier et positionne les drapeaux en fonction du résultat.

Si A est le premier opérande et B, le second :

drapeau positionné quand
CF A < B (non signé)
ZF A == B
SF A < B (signé, sans débordement)
OF débordement lors d'une soustraction signée

Les branchements

Par défaut, le code en x86-64 s'exécute séquentiellement, de haut en bas.

Mais il existe de nombreuses situations où il est nécessaire de modifier ce comportement. Par exemple, pour exécuter un ensemble d'instructions différent en réponse à une condition.

Dans les langages de plus haut niveau, cela se fait généralement à l'aide d'abstractions comme les conditionnelles if...else. Celles-ci n'existent cependant pas en x86-64.

À la place, x86-64 fournit des instructions qui transfèrent effectivement l'exécution vers un autre endroit du code. C'est ce qu'on appelle le branching.

Note

Nous avons déjà vu deux instructions de ce type : call et ret.

Lorsqu'une fonction est appelée, l'exécution est transférée de l'appelant vers la fonction appelée. Et, au retour, l'exécution est transférée de nouveau vers l'appelant.

Si aucun ret n'est trouvé, l'exécution passe d'une fonction à la suivante. Cela peut parfois servir à optimiser le flux du code.

Le saut inconditionnel

L'instruction jmp transfère inconditionnellement l'exécution du programme vers un autre point du code. Son unique opérande est une étiquette qui contient l'adresse du point où l'exécution se poursuivra.

Prenons par exemple la fonction suivante :

fn:
    mov rax, 5
    jmp end

    add rax, 10
end:
    ret

Quand fn est appelée, l'exécution commence à mov rax, 5. Cela fixe à 5 la valeur de rax à cet instant.

L'instruction suivante est jmp end, qui transfère l'exécution vers l'étiquette end.

Après end, l'instruction suivante est ret, qui transfère l'exécution de retour vers la fonction appelante.

Remarque que, comme add rax, 10 se trouve après jmp end et avant end, elle n'est jamais exécutée. La valeur de rax au retour de fn est 5.

Le saut conditionnel

La famille d'instructions jcc transfère l'exécution du programme vers un autre point uniquement si une condition précise est remplie. Sinon, l'exécution se poursuit séquentiellement.

Chaque condition correspond à un ou plusieurs drapeaux de rflags. Certaines variantes de jcc testent qu'un drapeau est positionné, d'autres qu'il est effacé.

Le cc dans jcc n'est pas littéral : il désigne le suffixe précis associé au drapeau testé.

Il existe de nombreux suffixes, et beaucoup testent la même condition qu'un autre. Certains se réfèrent directement à un drapeau, de sorte que l'instruction saute vers une étiquette si ce drapeau est positionné :

suffixe saute si
z ZF == 1
c CF == 1
s SF == 1
o OF == 1

Beaucoup d'autres sont choisis pour renvoyer à leur sens dans une instruction cmp. Par exemple :

instruction suffixe saute si
cmp A, B e A == B
cmp A, B l A < B (signé)
cmp A, B b A < B (non signé)
cmp A, B g A > B (signé)
cmp A, B a A > B (non signé)

Il est possible d'ajouter e après l, b, g ou a pour inclure l'égalité dans la condition :

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

Pour tous les suffixes, il existe des variantes qui vérifient le comportement opposé. Elles ont la même syntaxe, mais avec un n devant le suffixe.

Par exemple, jnz saute quand ZF n'est pas positionné. De même, jnae saute quand A n'est pas >= B (A et B étant interprétés comme des entiers non signés).

Note

Certains suffixes sont des alias désignant les mêmes conditions. Par exemple, jz (suffixe z, pour ZF) et je (suffixe e, pour égal) sautent tous les deux quand ZF est positionné. C'est parce que, avec cmp, ZF est positionné quand la soustraction donne zéro, ce qui correspond au fait que les deux opérandes sont égaux.

D'autres suffixes, en revanche, testent une combinaison de drapeaux et ne peuvent pas être directement remplacés par un suffixe portant sur un seul drapeau.

Préfère le suffixe qui décrit le mieux la sémantique de ta comparaison.

Les étiquettes locales

Les étiquettes sont visibles dans tout le fichier source ; elles ne sont pas locales à une fonction. Il est donc impossible de réutiliser le nom d'une étiquette.

Pour imiter le comportement d'une étiquette locale, NASM dispose d'une notation spéciale pour une étiquette déclarée avec un point (.) devant son nom. Cette notation définit une étiquette qui inclut implicitement le nom de l'étiquette non pointée précédente :

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

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

Il reste possible de sauter vers cette étiquette depuis n'importe où dans le code en utilisant son nom complet, par exemple jmp fn1.example.

Cependant, un saut qui utilise la partie de l'étiquette à partir du point se fera vers l'étiquette située à l'intérieur de la fonction englobante. Par exemple, .example se comporte comme si elle était locale à la fonction :

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

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

Note qu'une étiquette sans point à l'intérieur d'une fonction définit en pratique une autre fonction :

section .text
fn1:
    ...
.example: ; this is 'fn1.example'
    ...
non_dotted:
    ...
.example: ; this is 'non_dotted.example'
    ...
    ret
Modifie via GitHub Le lien s'ouvre dans une nouvelle fenêtre ou un nouvel onglet

Apprends Conditionnelles