Assembleur

Table of Contents

1 Conventions d’appels System V AMD64 ABI

Ces conventions sont suivies par Solaris, Linux, FreeBSD et macOS. Ces conventions doivent être respéctées pour s’interfacer avec le système d’exploitation.

  • Les arguments des fonctions sont passés dans les registres rdi, rsi, rdx, rcx, r8, r9, r10. Les arguments supplémentaires sont passés sur la pile.
  • Les arguments de type float sont passés dans les registres xmm0 à xmm7.
  • Si l’appelée utilise les registres rbx, rbp, ou r12-15, leur valeur originale doit être restaurée avant de quitter le corps de la fonction (les autres registres sont supposés être pris en charge par la fonction appelante).

2 Entrée

Commençons par voir à quoi ressemble un programme en assembleur. Au passage, on verra aussi comment faire des appels système relativement facilement. Téléchargez le fichier http://www.lsv.fr/~hondet/asm-starter.s. Demandez vous ce qu’il fait, et vérifiez le comportement avec ddd. Pour, par exemple, voir les registres et leur contenu, faites un click droit sur une ligne du code source, et “set breakpoints”; cliquez ensuite sur “Run”; et une fois au breakpoint, cliquez sur “Status” puis sur “Register”. Cliquez sur “Stepi” pour avancer instruction par instruction. Remarquez le changement de valeur du registre rcx ou rdi. Il ne devrait pas y avoir de surprise.

3 Initiation à l’assembleur

Écrivons petit à petit une fonction qui somme un nombre variable d’arguments.

3.1 Une simple addition: pas de pile

  1. Écrire un programme qui renvoit un entier arbitraire fixé dans le code. Pour vérifier son bon fonctionnement, en supposant que l’exécutable se nomme a.out

    ./a.out
    echo $?
    

    devrait afficher l’entier choisi ($? contient le dernier code de retour).

  2. Implémenter l’addition de deux entiers uniquement à l’aide de l’opérateur d’incrémentation et de décrémentation. Les arguments pourront dans un premier temps être placés dans des registres (donc déclarés dans le code), par exemple,

        movq    $3, %rbx
        movq    $2, %rcx
    

    pour par la suite additioner 3 et 2. Le résultat devra être le code de retour.

  3. Utiliser des constantes déclarées dans la section .data plutôt que des valeurs codées en dur.
  4. Introduire (si vous ne l’avez pas fait naturellement) une fonction add.

3.2 Une addition variadique: amenez la pile

  1. Écrire une fonction adds prenant ses deux arguments sur la pile (vous pouvez utiliser popq, même s’il existe un autre moyen).
  2. Écrire une fonction addv qui prend son premier argument dans rdi et le reste sur la pile et les somme. Si rdi = n, addv somme les n arguments de la pile.
  3. Modifiez votre fonction pour utiliser le moins de registres possible, et utiliser la pile. Cela nécessite de s’allouer de la place sur la pile en entrant dans la fonction pour y stocker des variables locales. On va donc fair entrer en jeu la gestion des stack frames. Concrètement, on va utiliser le registre rbp pour indiquer le fond de la pile pour la fonction (et donc l’adresse la plus élevée à laquelle la fonctions peut accéder). Pour l’utiliser, on va, pour tout appel de fonction nécessitant des variables locales,

    1. sauvegarder le pointeur de base de pile rbp pour pouvoir le restaurer en sortant,
    2. mettre le pointeur de base de pile à la même valeur que le pointeur de sommet de pile

    Ainsi, rbp indique la base d’une pile à laquelle on peut accéder. En se débrouillant bien, on n’utilise que les registres rsi, rax et rbp.

En pratique, il sera aussi nécessaire de décaler rsp pour le sortir de l’espace qu’on se réserve sur la pile, pour pouvoir appeler d’autres fonctions.

4 S’il en faut plus

Je vous invite à regarder le TP donné il y a deux ans qui consiste à écrire un débogueur, disponible à http://www.lsv.fr/~hondet/6-bonus.pdf.

Author: Rémy Poulain & Gabriel Hondet

Created: 2019-11-06 Wed 15:29

Validate