• Aucun résultat trouvé

Procédures

Dans le document Cours assembleur Avec Rappel en Pdf (Page 36-41)

8.1 Les instructions call et ret

Les instructions call et ret permettent respectivement d'appeler et de sortir d'une procédure. L'opérande qui suit call est l'adresse symbolique de la procédure. Traditionnellement, les paramètres de la procédure sont passés par la pile.

call empile tout d'abord l'adresse de la prochaine instruction (le contenu de %rip) et remplace le contenu de rip par l'adresse donnée en opérande. ret

dépile et place la valeur dépilée dans rip, ce qui provoque (sauf erreur de programmation) le retour à l'endroit où la procédure à été appelée (instruction qui suit le « call adresse »). Comme la pile n'est pas uniquement utilisée pour sauvegarder l'adresse de retour, mais aussi pour stocker les paramètres et les

14 Il existe une version 16 bits de ce registre (sp), mais nous ne pouvons l'utiliser que dans un mode particulier du processeur que nous ne verrons pas ici.

variables locales de la procédure, il est important de s'assurer que le haut de la pile contient bien l'adresse de retour avant l'exécution de ret.

ret peut avoir une opérande n de type « constante entière ». Dans ce cas, ret

dépilera l'adresse de retour, puis n octets avant de retourner à la procédure appelante. Le registre rbp (re-extended base pointer) est souvent utilisé par le programmeur pour accéder aux différentes zones de la pile, il pointe généralement sur l'adresse de retour de la procédure.

Voici un exemple de procédure qui prend 2 entiers non signés comme paramètres (par la pile) et renvoie le maximum des 2 valeurs :

appel:

# Empilage des paramètres de la procédure Maximum push UnsignedQuadWord_a

push UnsignedQuadWord_b

call Maximum # appel procédure Maximum

popq %rax # résultat dans rax

# <<suite de la procédure appelante >>

retq # retour à la procédure appelante Maximum:

movq %rsp, %rbp

# rbp pointe sur l'adresse de retour pushq %rax # sauve ancienne valeur de rax pushq %rbx # sauve ancienne valeur de rbx movq 8(%rbp), %rbx # copie le paramètre

# b dans rbx

movq 16(%rbp), %rax # copie le paramètre # a dans rax

cmpq %rax, %rbx # compare le 1er et le 2e # paramètre

jae bmax # Si b >= a alors saute à bmax

amax: pushq %rax # empile a

jmp FinMax

bmax: pushq %rbx # empile b

FinMax: popq %rax # valeur max -> rax movq %rax, 8(%rbp)

# valeur max -> 1er paramètre

popq %rbx # récupère ancienne valeur de %rbx popq %rax # récupère ancienne valeur de %rax retq $8 # dépile le 2e paramètre avant de # retourner à la procédure appelante

Voici un schéma qui illustre l'exécution de procédures imbriquées. Les chiffres représentent l'ordre d'exécution du code :

Et voici l'évolution de la pile lors de l'exécution schématisée ci-dessus : 1. {} 2. {adresse de 5} 3. {adresse de 5, adresse de 4} 4. {adresse de 5} 5. {} 6. {adresse de 7} 7. {} procA : .... call C .... ret

2

4

procC : ... ret .... call procA ... call procB ... ret procB : ... ret

1

5

3

6

7

8.2 Les interruptions et les exceptions

Il nous manque encore un dispositif pour permettre au processeur de réagir en temps réel à divers évènements, liés aux périphériques (appui d'une touche sur le clavier, clic ou mouvement de la souris, etc.) ou à l'état du processeur lui- même (division par zéro, accès à une zone mémoire protégée, etc.). Un simple

balayage permanent de ces très nombreuses données gaspillerait un montant

considérable de ressources.

Heureusement, tous les processeurs actuels possèdent un jeu d'instructions qui permettent une gestion événementielle de cette problématique. Par exemple, un périphérique, lorsqu'il est sollicité par l'utilisateur, envoie un signal (qu'on appelle interruption) au microprocesseur, qui peut alors interrompre l'exécution du programme courant, exécuter une routine du système d'exploitation et revenir au programme courant lorsque l'interruption a été traitée.

En réalité, n'importe quel programme peut générer des interruptions logicielles et des exceptions. Mais nous ne rentrerons pas ici dans des détails qui relèveraient plus d'un cours sur les systèmes d'exploitation que d'un cours sur l'assembleur et le langage machine.

Nous allons limiter cette section à la description de l'instruction int, qui va nous permettre de communiquer avec le système d'exploitation (pour les exemples, nous prendrons GNU/Linux).

L'instruction int a un comportement similaire à l'instruction call. Le registre

rip va être modifié pour permettre l'exécution d'une procédure, mais l'adresse

de l'instruction int est mémorisée, de telle sorte qu'à la sortie de la procédure,

rip prend pour valeur l'adresse de l'instruction qui suit l'instruction int.

La syntaxe de l'instruction int est la suivante : int Constante

Constante est un entier qui désigne le numéro du gestionnaire d'interruption à appeler. La valeur de certains registres sera utilisée par le gestionnaire d'interruptions afin de sélectionner la procédure appropriée et de lui transmettre ses paramètres.

Sous GNU/Linux, par exemple, int $0x80 va appeler le gestionnaire d'interruption du système d'exploitation (le gestionnaire de numéro hexadécimal 80, soit 128 en décimal) . Le numéro de fonction système est donné par %eax et les paramètres sont transmis via les registres %ebx,%ecx, %edx,%esi,%edi (dans cet ordre). Lorsqu'il y a plus de 5 paramètres, %ebx contient tout simplement l'adresse des paramètres.

Voici par exemple, un petit programme en assembleur, destiné à être assemblé, lié et exécuté sous GNU/Linux :

.data # directive de création d'une zone de donnée btlm: # adresse symbolique pointant sur la chaîne:       .string "Bonjour tout le monde!\n" .text # directive de création d'une zone  # d'instructions .globl main # directive de création d'une étiquette  # de portée globale main: # main est l'adresse de début du programme          movl    $4,%eax # sélection de la fonction  # write du système         movl    $1,%ebx # dernier paramètre de write : # stdout         movl    $btlm,%ecx # premier paramètre  # de write : l'adresse de # la chaîne de caractères à # afficher         movl    $23,%edx # le nombre de caractères à # afficher : 23         int     $0x80 # appel de l'interruption  # 128 ­> GNU/Linux         movl    $1,%eax # sélection de la fonction  # exit du système         xorl    %ebx,%ebx # mise à zéro du 1er paramètre # en utilisant # xor, c'est à dire ou exclusif         int     $0x80 # appel de l'interruption  # 128 ­> GNU/Linux         ret # fin du programme et retour au système

Dans le document Cours assembleur Avec Rappel en Pdf (Page 36-41)

Documents relatifs