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 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 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 |
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.
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.
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.
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).
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 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