Introduction

Ce didacticiel est destiné aux nouveaux utilisateurs de la Simple Virtual Machine.

Dans ce didacticiel, vous allez améliorer les capacités du code à accéder à la mémoire de la machine virtuelle.

Le temps de lecture de ce didacticiel est estimé à 30 minutes si l'écriture d'une application simple a déjà été étudiée ainsi que les manipulations basiques de la mémoire.

Mise en place

Pour commencer, créez le canevas de l'application dans le fichier exécutable pointeurs.svm en utilisant ce code :

#!/usr/bin/env svm
DESCRIPTION
Memory examples
END
LOG
DEBUG "Memory" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmint.so"
PLUGIN "svmstr.so"
OPTION -i MULTIPLE INT integers
OPTION -s MULTIPLE STR strings
PROCESS "application"
	CODE "main" INLINE
		:debug BREAK
	END
	MEMORY ".*"
END

Adresses mémoire (rappel)

Lancez l'application en mode débugueur :

./pointeurs.svm -d 8080 -i 3 -i 1 -i 4 -i 1 -i 5 -s a -s b -s c

Dans le débugueur, en utilisant la fenêtre des noyaux (Kernels), sélectionnez le noyau puis affichez la fenêtre de la mémoire :

Memory
Main menu
Breakpoints
Machine
Schedulers
Processes
Kernels
Events
Plugins
Windows list
Kernel list
PROCESS application - Kernel main (D)
Kernel main - P application
State: D, transmit_interruption
Processor
Memory
Memory - K main - P application
AddressTypeValue
&0INT3
&1INT1
&2INT4
&3INT1
&4INT5
&5STR"a"
&6STR"b"
&7STR"c"
AliasPointer
integers&0*5
strings&5*3
Address:
Display
Aliases
P
Focus
Back
Clear

Les valeurs créées à partir des options de la ligne de commande sont placées à différentes adresses mémoire : à chaque valeur est associé un nombre qui sert d'identifiant unique pour accéder à la valeur depuis les instructions exécutées.

Par exemple, à l'adresse &6 se trouve la valeur de type chaine de caractères "b". Vous pouvez entrer le numéro de l'adresse que vous voulez mettre en surbrillance dans la petite zone de texte en bas de la fenêtre de mémoire du débugueur, et cliquer sur le bouton "Display".

Pointeurs

Dans une application, il peut être utile de pouvoir indiquer où se situent dans la mémoire un certain nombre de valeurs ayant des adresses contigües.

Pour cela, il faut un objet qui contienne une adresse de départ et le nombre d'adresses après cette première adresse : un tel objet existe et s'appelle un pointeur.

Dans le débugueur de l'application exemple, en dessous des adresses et leurs valeurs associées se trouvent un petit tableau dont la deuxième colonne contient des pointeurs. Vous pouvez cliquer sur le bouton ">" à droite de chaque pointeur pour afficher les adresses indiquées par chaque pointeur.

Un pointeur permet de référencer une zone mémoire continue, à partir d'une adresse de départ et sur un certain nombre d'adresses.

Les pointeurs sont notés par une adresse et un entier séparés par le symbole *.

Arithmétique d'adresse et de pointeur

Ce système pourtant très rustique d'adresses et de pointeurs possède une vraie puissance pour accéder aux différentes valeurs de la mémoire, au travers d'opérations spécifiques.

Construire un pointeur

La première possibilité est de construire un pointeur à partir de son adresse de départ et de sa taille. Modifiez le code pour obtenir (la signification de la première instruction sera donnée plus bas dans ce didacticiel) :

#!/usr/bin/env svm
DESCRIPTION
Memory examples
END
LOG
DEBUG "Memory" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmint.so"
PLUGIN "svmstr.so"
OPTION -i MULTIPLE INT integers
OPTION -s MULTIPLE STR strings
PROCESS "application"
	CODE "main" INLINE
		:memory PTR/p
		&3*4 -> &p
		:debug BREAK
	END
	MEMORY ".*"
END

Puis relancez l'application comme précédemment et ouvrez le débugueur sur la fenêtre mémoire :

Memory
Memory - K main - P application
AddressTypeValue
&0INT3
&1INT1
&2INT4
&3INT1
&4INT5
&5STR"a"
&6STR"b"
&7STR"c"
&8PTR&3*4
AliasPointer
integers&0*5
p&8*1
strings&5*3
Address:
Display
Aliases
P
Focus
Back
Clear

Le pointeur commençant à l'adresse 3 sur 4 adresses a tout simplement été créé en écrivant dans le code &3*4. Dans la fenêtre de la mémoire, vous pouvez cliquer sur le bouton ">" à droite de ce pointeur pour mettre en surbrillance la zone mémoire qu'il représente.

Utilisation des alias

Dans une application, certaines zones mémoire sont privilégiées, et doivent pouvoir être accessibles facilement depuis n'importe quel endroit du code.

Pour cela, la machine virtuelle offre la possibilité de nommer certains pointeurs. Ces noms, définis de manière unique, portent le nom d'alias et peuvent être utilisés comme des pointeurs.

Par ailleurs, les valeurs associées aux options et arguments de la ligne de commande sont disponibles au travers d'alias définis automatiquement par la machine virtuelle au démarrage de l'application.

Modifiez le code de l'application pour obtenir :

#!/usr/bin/env svm
DESCRIPTION
Memory examples
END
LOG
DEBUG "Memory" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmint.so"
PLUGIN "svmstr.so"
OPTION -i MULTIPLE INT integers
OPTION -s MULTIPLE STR strings
PROCESS "application"
	CODE "main" INLINE
		:memory PTR/p
		integers -> &p
		:debug BREAK
	END
	MEMORY ".*"
END

Puis, encore une fois, relancez l'application avec les mêmes options dans le débugueur. Cette fois, la fenêtre mémoire montre :

Memory
Memory - K main - P application
AddressTypeValue
&0INT3
&1INT1
&2INT4
&3INT1
&4INT5
&5STR"a"
&6STR"b"
&7STR"c"
&8PTR&0*5
AliasPointer
integers&0*5
p&8*1
strings&5*3
Address:
Display
Aliases
P
Focus
Back
Clear

Le pointeur placé à l'adresse &8 contient cette fois le pointeur qui se trouve à droite de l'alias "integers".

Décomposer un pointeur

L'opération inverse peut être faite sur un pointeur : il peut être déconstruit pour récupérer son adresse de départ ou sa taille. Modifiez une nouvelle fois le code :

#!/usr/bin/env svm
DESCRIPTION
Memory examples
END
LOG
DEBUG "Memory" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmint.so"
PLUGIN "svmstr.so"
OPTION -i MULTIPLE INT integers
OPTION -s MULTIPLE STR strings
PROCESS "application"
	CODE "main" INLINE
		:debug EXPLAIN &integers*SIZE strings
	END
	MEMORY ".*"
END

Et une nouvelle fois, tel un running gag, relancez l'application avec les mêmes options dans le débugueur. En revanche, cette fois, ouvrez plutôt la liste des points d'arrêt :

Memory
Main menu
Breakpoints
Machine
Schedulers
Processes
Kernels
Events
Plugins
Windows list
Breakpoint list
Explain:
At <main:1/0>
Value &integers*SIZE strings
& integers => &0*5 *SIZE strings
 &&0*5 => &0 *SIZE strings
&0*SIZE  strings => &5*3 
&0* SIZE &5*3 => 3 
 &0*3 => &0*3 
&0*3

Le pointeur calculé par l'expression du point d'arrêt est constitué de l'adresse de départ du pointeur de l'alias "integers" (obtenue avec l'opérateur &) et de la taille du pointeur de l'alias "strings" (obtenue avec l'opérateur SIZE).

Accéder à une valeur dans la zone d'un pointeur

Indiquer une zone mémoire grâce à un pointeur a l'avantage d'être plutôt compact pour désigner dans la mémoire plusieurs adresses mémoire. Cependant, lorsque le code de l'application doit accéder aux différentes valeurs du pointeur, il va falloir lui fournir les adresses contenues dans le pointeur.

Une nouvelle fois, suivez la procédure décrite précédemment avec le code :

#!/usr/bin/env svm
DESCRIPTION
Memory examples
END
LOG
DEBUG "Memory" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmint.so"
PLUGIN "svmstr.so"
OPTION -i MULTIPLE INT integers
OPTION -s MULTIPLE STR strings
PROCESS "application"
	CODE "main" INLINE
		:debug EXPLAIN (integers/0)
		:debug EXPLAIN (integers/2)
		:debug EXPLAIN (integers/5)
		:debug EXPLAIN (integers/-1)
		:debug EXPLAIN (strings/1)
	END
	MEMORY ".*"
END

Après avoir passé tous les points d'arrêt, la fenêtre des points d'arrêt indique :

Memory
Main menu
Breakpoints
Machine
Schedulers
Processes
Kernels
Events
Plugins
Windows list
Breakpoint list
Explain:
At <main:1/0>
Address (integers/0)
( integers => &0*5 /0)
(&0*5/ 0 => 0 )
 (&0*5/0) => &0 
&0
Explain:
At <main:1/1>
Address (integers/2)
( integers => &0*5 /2)
(&0*5/ 2 => 2 )
 (&0*5/2) => &2 
&2
Explain:
At <main:1/2>
Address (integers/5)
( integers => &0*5 /5)
(&0*5/ 5 => 5 )
 (&0*5/5) => ... 
Runtime error (MEMORY): Index 5 is outside pointer &0*5.
Explain:
At <main:1/3>
Address (integers/-1)
( integers => &0*5 /-1)
(&0*5/ -1 => -1 )
 (&0*5/-1) => ... 
Runtime error (MEMORY): Index -1 is outside pointer &0*5.
Explain:
At <main:1/4>
Address (strings/1)
( strings => &5*3 /1)
(&5*3/ 1 => 1 )
 (&5*3/1) => &6 
&6

Cette succession de calculs d'adresses à l'intérieur de pointeurs montre que :

Cette dernière propriété est très importante, car à long terme, elle assure de l'intégrité des accès en mémoire à travers les différents pointeurs manipulés par le code.

Notez enfin que cette notation utilise un caractère division (/) qui n'a pas la même signification que celui présent sur l'instruction :memory et qui sera détaillé ultérieurement.

Ecriture mémoire en utilisant un pointeur

Il est possible d'écrire en mémoire plusieurs valeurs d'un seul coup en utilisant une autre forme de l'instruction ->.

Modifiez le code de l'application pour obtenir :

#!/usr/bin/env svm
DESCRIPTION
Memory examples
END
LOG
DEBUG "Memory" STYLE "default"
PLUGIN "svmcom.so"
PLUGIN "svmint.so"
PLUGIN "svmstr.so"
OPTION -i MULTIPLE INT integers
OPTION -s MULTIPLE STR strings
PROCESS "application"
	CODE "main" INLINE
		:debug BREAK
		[ 17 , 42 , , "A", "B" ] -> &2*5
	END
	MEMORY ".*"
END

Puis utilisez le débugueur pour voir l'action de cette instruction : toutes les valeurs écrites entre les crochets sont écrites d'un seul coup dans la mémoire à l'endroit indiqué par le pointeur, à condition que le nombre de valeur corresponde à la taille du pointeur et que les types correspondent avec ceux de la mémoire. En cas de tentative d'écriture mal formée, aucune valeur n'est écrite en mémoire et une erreur est générée.

Si une valeur est manquante, cela enlève la valeur précédemment écrite en mémoire.

Conclusion

Vous venez de voir comment accéder plus finement à la mémoire de la machine virtuelle.

La puissance du langage de la machine virtuelle repose en grande partie sur la capacité à accéder à des adresses grâce à une syntaxe compacte et versatile. En effet, les différents opérateurs présentés dans ce didacticiel peuvent être combinés pour atteindre des adresses après plusieurs indirections depuis un pointeur initial.