lundi 23 octobre 2017

Chapitre 3 : affichage des zones de la mémoire



Vous avez bien programmé le dernier exercice du chapitre précédant ?  Car le vidage de tous les registres est une aide bien précieuse pour trouver les erreurs d’un programme. Personnellement je trouve cela plus simple que d’utiliser le débogueur GDB. Dans ce chapitre, nous allons voir l’affichage des zones de la mémoire car c’est le deuxième point important pour le débogage ou pour vérifier le contenu des zones après l’appel d’une fonction.
Nous afficherons sur une ligne : l’adresse du début d’un bloc de 16 octets (cad une adresse se terminant par 0), ce bloc contenant l’adresse de la zone demandée. Les 16 octets seront affichés d’abord en hexadécimal puis en ASCII.
Voici un exemple du résultat :

 Voici le  lien pour le source du programme.
La fonction d’affichage , nécessite le passage de l’adresse de la zone et le nombre de bloc de 16 octets à afficher.
Nous allons passer ces paramètres par la pile pour voir le fonctionnement de celle-ci. Mais les arguments pouvaient être passé par les registres r0 et r1.
Donc dans le programme principal, nous passons les 2 paramètres par un push {r0,r1} et nous appelons la routine d’affichage. Dans cette routine, nous sauvegardons les 2 registres fp et lr comme déjà vu puis nous alimentons le registre fp (Frame pointer ou pointeur de contexte) avec l’adresse de la pile (registre sp) plus 8 octets. Ces 8 octets correspondent à la sauvegarde des 2 registres fp et lr. Nous faisons une addition pour retrouver l’adresse de la pile qu’elle avait au début de la procédure. En effet, lors du stockage sur la pile, les adresses font en diminuant (voir la documentation).
Maintenant le registre fp contient l’adresse du premier paramètre passé à la fonction et fp+4octets contient l’adresse du deuxième paramètre car nous avons utilisé un seul push pour passer les 2 registres. Si nous avions effectué 2 push il faut inverser les 2 registres. J’ai mis ces instructions en commentaire mais vous pouvez les remettre pour faire des tests.
Mais d’abord, nous allons préparer l’affichage d’une identification du vidage. Je voulais afficher le N° de ligne du source mais hélas avec l’assembleur as, il n’existe pas d’instruction permettant d’identifier ce N° (dans d’autres assembleurs on trouve par exemple __LINE__). Donc je vais afficher l’instruction d’appel de notre routine et pour la trouver, nous allons nous servir du registre lr qui contient l’adresse de retour cad l’adresse se trouvant juste après l’adresse de l’appel. Donc il suffit d’enlever 4 octets à l’adresse contenue dans lr pour avoir notre résultat puis le convertir en hexa et le placer dans la ligne d’entête.
Nous convertissons aussi en hexa l’adresse demandée et contenue dans le registre fp. Nous plaçons le résultat dans la ligne d’entête puis nous affichons celle çi par la fonction générale d’affichage déjà décrite.
Ensuite nous stockons l’adresse du début du vidage (dans r2) et le nombre de bloc de 16 octets demandés (dans r6) à partir du registre fp et fp+4octets. Puis nous calculons le début d’un bloc sur un multiple de 16 octets et contenant le début du vidage demandé. Pour cela nous divisons par 16 en déplaçant les bits de r2 de 4 bits vers la droite. Puis nous multiplions par 16 en déplaçant 4 bits vers la gauche, ce qui a pour effet de mettre à zéro le dernier chiffre d’ l’adresse de vidage.

/*calculer debut du bloc de 16 octets*/
                mov r1, r2, ASR #4      /* r1 <- (r2/16) */
                mov r1, r1, LSL #4      /* r1 <- (r2*16) */

Pour signaler sur la ligne, l’octet correspondant exactement à l’adresse demandée, nous allons mettre une étoile juste devant. Pour cela nous calculons le déplacement entre l’adresse et le début du bloc précédemment calculé, nous le multiplions par 3.
Cela parait compliqué à comprendre mais revoir le résultat des 2 premières lignes plus haut.
Remarquez l’instruction strb r0,[r7] le b indique que nous ne stockons qu’un octet à l’adresse contenue dans le registre r7.
Maintenant, nous rentrons dans l’exécution de 3 boucles, une pour gérer l’affichage des blocs, une autre interne à la première pour gérer l’affichage des 16 octets en hexa (ce qui représente 2 chiffres) suivie d’une autre pour gérer l’affichage des 16 mêmes octets en Ascii si cela est possible.
Pour l’affichage en hexa, nous utilisons une autre méthode que celle vu précédemment car il n’y a que 2 chiffres à convertir et cela nous permet de manipuler les instructions.
Quand la ligne est complète, nous l 'affichons avec la routine habituelle et nous bouclons sur la préparation du bloc suivant si le nombre de bloc n’est pas atteint. Ah oui ! il faut d’abord effacer l’étoile pour qu’elle n’apparaisse pas sur les lignes suivantes.
En fin de routine, nous restaurons les registres utilisés puis les registres fp et lr et nous revenons au programme principal. La routine fonctionne correctement quand nous l’appelons du programme principal mais se termine par une erreur de segmentation si nous l’appelons d’une autre routine. Soit par le débuggeur soit en insérant la routine d’affichage des registres écrite au chapitre précédant, nous arrivons à la conclusion que l’anomalie n’est pas dans la routine d’affichage mais se produit lors du retour de la routine appelante. Plus exactement, nous nous rendons compte que l’adresse de la pile est différente avant l’appel de la routine affmemoire et après le retour de la routine ce qui entraine une mauvaise restauration du registre lr et une erreur lors du retour au programme appelant. Et là illumination : nous avons stocké sur la pile 2 paramètres que nous n’avons pas dépilés dans la routine. Il faut donc ajouter 8 octets à l’adresse de la pile tout en fin de routine affmemoire (instruction add sp, sp, #8) et tout fonctionne.
Exercice : Ajouter un 3 ième paramètre qui contient l’adresse d’une chaine d’identification. Faire apparaitre cette chaine à la place de l’adresse de l’instruction dans l’entête de l’affichage.
            Vider différents adresses de la Ram : certaines sont inaccessibles. En fait la question est : quelle est la plage d’adresses autorisée à mon programme (à ce jour je n’ai pas la réponse !!).
         Écrire une routine de vidage du registre d’état en indiquant l’état des drapeaux : Exemple :  Carry actif ou Carry inactif etc..
        Ecrire une routine qui vide 10 adresses avant l’adresse courante de la pile et 10 adresses après en hexa. Passer le nombre d’adresse en paramètre.

Aucun commentaire:

Enregistrer un commentaire