Types d'adresses

Cet article résume les différents types d'adresses dans l'architecture IA-32. Cette architecture utilise principalement 3 types d'adresses:

On peut résumer le processus de traduction comme sur le schéma suivante.

x86 Address Translation

Segmentation

Le processeur défini des registres de segment:

Ces registres de segment font tous 16 bits de long, ces 16 bits sont appelés Selecteur de Segment, qui, comme on peut le voir sur le schéma suivant, consiste en un index, un bit IT ainsi qu'un champ RPL. Le bit IT est dans notre cas toujours à 0 pour pointer vers la GDT, s'il était à 1 il pointerait sur la LDT que l'on ne va pas aborder en détails ici. Le champ RPL Requested Privilege Level, permet de spécifier le privilège attendu de notre segment, on ne rentrera pas dans le détail ici.

Comme expliqué dans l'introduction, les instructions utilisent des adresse logiques et donc utilise un segment implicitement, dépendamment du type d'instruction:

1
2
3
4
5
6
7
8
    # Pas de sélecteur de segment spécifier:
    movl $42, (%eax)
    # C'est en réalité équivalent à:
    movl $42, %ds:(%eax)
    # Mais on peut choisir le segment explicitement:
    movl $42, $gs:($eax)
    # Par exemple, les instructions push/pop utilise le Stack Segment (SS)
    pushl $42

On voit dans l'exemple ci-dessus que les segments se cachent dans les instructions les plus communes. Et qu'il est possible de les expliciter. Dans l'exemple, en plus du sélecteur, vous aurez deviné que l'offset de l'adresse logique est défini à la valeur du registre EAX.

Parlons maintenant de la GDT. La Global Descriptor Table est une table en mémoire constituée jusqu'à 8192 descripteurs de 8 octets chacun (max 65 Kio). Pour connaître l'adresse de cette structure, le processeur utilise un registre interne appelé GDTR qui contient à la fois l'adresse de base mais également le nombre de descripteur maximum. Ce registre est typiquement défini au chargement du kernel par l'instruction lgdt.

Comme on peut le voir sur le schéma suivant, le descripteur 0 est invalide, et les descripteur suivant sont constitués d'une multitude de champs permettant déclarer l'address de base, la limite, le type et des flags pour chaque segment.

Un sélecteur de segment référence ainsi un descripteur dans la GDT, qui permet ainsi de connaître l'adresse linéaire de base du segment. Le processus de traduction complet est décrit sur le schéma ci-dessous.

x86 Segmentation

Un exemple de code peut permettre de mieux comprendre le fonctionnement. On suppose ici que notre GDT contient 2 entrées, le descripteur zero étant invalide et le descripteur 1 définissant comme base 0x0004000 et limite 0x40.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
    # On charge le sélecteur 0x0008 (index = 1, 
    # IT = 0, RPL = 0) dans DS:
    movw $0x0008, %ds
    # On veut écrire à l'offset 0x10 du segment:
    movl $0x10, %eax
    # On écrit en réalité à l'adresse linéaire 0x4010:
    movl $42, (%eax)
    # On peut également générer une exception #GP
    # si on dépasse la limite de taille du segment.
    movl $0x50, %eax
    movl $42, (%eax)
    # Comme vu précédemment on peut utiliser l'écriture
    # alternative:
    movl $42, %ds:(%eax)

Pagination

TODO !

<