Architecture x86

Le processeur Intel x86 utilise une architecture CISC (Jeu d’instructions complexes), ce qui signifie qu’il existe un nombre modeste de registres à usage spécial au lieu de grandes quantités de registres à usage général. Cela signifie également que les instructions complexes à usage spécial prédomineront.

Le processeur x86 trace son héritage au moins aussi loin que le processeur Intel 8080 8 bits. De nombreuses particularités dans le jeu d’instructions x86 sont dues à la compatibilité descendante avec ce processeur (et avec sa variante Zilog Z-80).

Microsoft Win32 utilise le processeur x86 en mode plat 32 bits. Cette documentation se concentre uniquement sur le mode plat.

Registres

L’architecture x86 se compose des registres d’entiers non privilégiés suivants.

Eax

Accumulateur

Ebx

Registre de base

ecx

Registre de compteurs

Edx

Registre de données : peut être utilisé pour l’accès au port d’E/S et les fonctions arithmétiques

Esi

Registre d’index source

Edi

Registre d’index de destination

Ebp

Registre des pointeurs de base

Esp

Pointeur de pile

Tous les registres d’entiers sont de 32 bits. Toutefois, la plupart d’entre eux ont des sous-inscriptions 16 bits ou 8 bits.

ax

Faible 16 bits d’eax

Bx

Faible 16 bits d’ebx

Cx

Faible 16 bits d’ecx

Dx

Faible 16 bits d’edx

si

Faible 16 bits d’esi

Di

Faible 16 bits d’edi

Bp

Faible 16 bits d’ebp

sp

Faible 16 bits d’esp

Al

Faible 8 bits d’eax

Ah

Haute 8 bits de hache

Bl

Faible 8 bits d’ebx

Bh

High 8 bits de bx

Cl

Faible 8 bits d’ecx

ch

High 8 bits de cx

Dl

Faible 8 bits d’edx

Dh

High 8 bits de dx

Le fonctionnement d’une sous-inscription affecte uniquement la sous-inscription et aucune des parties en dehors de la sous-inscription. Par exemple, le stockage dans le registre ax laisse les 16 bits élevés du registre eax inchangés.

Lors de l’utilisation de ? (Évaluer l’expression) commande , les registres doivent être précédés d’un signe « at » ( @ ). Par exemple, vous devez utiliser ? @ax plutôt que ? ax. Cela garantit que le débogueur reconnaît ax comme un registre plutôt qu’un symbole.

Toutefois, le (@) n’est pas obligatoire dans la commande r (Registres). Pour instance, r ax=5 sera toujours interprété correctement.

Deux autres registres sont importants pour l’état actuel du processeur.

Eip

pointeur d’instruction

flags

flags

Le pointeur d’instruction est l’adresse de l’instruction en cours d’exécution.

Le registre des indicateurs est une collection d’indicateurs mono bits. De nombreuses instructions modifient les indicateurs pour décrire le résultat de l’instruction. Ces indicateurs peuvent ensuite être testés par des instructions de saut conditionnel. Pour plus d’informations, consultez Indicateurs x86 .

Conventions d’appel

L’architecture x86 a plusieurs conventions d’appel différentes. Heureusement, ils suivent tous les mêmes règles de conservation de registre et de retour de fonction :

  • Les fonctions doivent conserver tous les registres, à l’exception de eax, ecx et edx, qui peuvent être modifiés dans un appel de fonction, et esp, qui doivent être mis à jour conformément à la convention d’appel.

  • Le registre eax reçoit des valeurs de retour de fonction si le résultat est inférieur ou égal à 32 bits. Si le résultat est de 64 bits, le résultat est stocké dans la paire edx :eax .

Voici la liste des conventions d’appel utilisées sur l’architecture x86 :

  • Win32 (__stdcall)

    Les paramètres de fonction sont transmis sur la pile, poussés de droite à gauche, et l’appelé nettoie la pile.

  • Appel de méthode C++ natif (également appelé thiscall)

    Les paramètres de fonction sont transmis sur la pile, poussés de droite à gauche, le pointeur « this » est passé dans le registre ecx et le appelé nettoie la pile.

  • COM (__stdcall pour les appels de méthode C++)

    Les paramètres de fonction sont transmis sur la pile, envoyés de droite à gauche, puis le pointeur « this » est envoyé sur la pile, puis la fonction est appelée. L'appelé nettoie la pile.

  • __fastcall

    Les deux premiers arguments DWORD ou plus petits sont passés dans les registres ecx et edx . Les paramètres restants sont transmis sur la pile, poussés de droite à gauche. L'appelé nettoie la pile.

  • __cdecl

    Les paramètres de fonction sont transmis sur la pile, poussés de droite à gauche, et l’appelant nettoie la pile. La convention d’appel __cdecl est utilisée pour toutes les fonctions avec des paramètres de longueur variable.

Affichage des registres et des indicateurs du débogueur

Voici un exemple d’affichage du registre du débogueur :

eax=00000000 ebx=008b6f00 ecx=01010101 edx=ffffffff esi=00000000 edi=00465000
eip=77f9d022 esp=05cffc48 ebp=05cffc54 iopl=0         nv up ei ng nz na po nc
cs=001b  ss=0023  ds=0023  es=0023  fs=0038  gs=0000             efl=00000286

Dans le débogage en mode utilisateur, vous pouvez ignorer l’iopl et la dernière ligne entière de l’affichage du débogueur.

Indicateurs x86

Dans l’exemple précédent, les codes à deux lettres à la fin de la deuxième ligne sont des indicateurs. Il s’agit de registres mono bits qui ont diverses utilisations.

Le tableau suivant répertorie les indicateurs x86 :

Code d’indicateur Nom de l’indicateur Valeur État de l’indicateur Description
sur Indicateur de dépassement de capacité 0 1 nvov Aucun dépassement de capacité - Dépassement de capacité
df Balise de direction 0 1 updn Direction vers le haut - Direction vers le bas
if Indicateur d’interruption 0 1 diei Interruptions désactivées - Interruptions activées
sf Indicateur de signe 0 1 plng Positif (ou zéro) - Négatif
Zf Zéro indicateur 0 1 nzzr Non zéro - Zéro
af Indicateur de transport auxiliaire 0 1 naac Pas de transport auxiliaire - Transport auxiliaire
Pf Indicateur de parité 0 1 Pepo Parité impaire - Parité pair
Cf Indicateur de transport 0 1 nccy Pas de transport - Transport
Tf Indicateur d’interruption Si tf est égal à 1, le processeur déclenche une exception STATUS_SINGLE_STEP après l’exécution d’une instruction. Cet indicateur est utilisé par un débogueur pour implémenter le suivi en une seule étape. Il ne doit pas être utilisé par d’autres applications.
iopl Niveau de privilège d’E/S Niveau de privilège d’E/S Il s’agit d’un entier deux bits, avec des valeurs comprises entre zéro et 3. Il est utilisé par le système d’exploitation pour contrôler l’accès au matériel. Il ne doit pas être utilisé par les applications.

Lorsque des registres sont affichés à la suite d’une commande dans la fenêtre Commande du débogueur, il s’agit de l’indicateur status qui s’affiche. Toutefois, si vous souhaitez modifier un indicateur à l’aide de la commande r (Registres), vous devez y faire référence par le code de l’indicateur.

Dans la fenêtre Registres de WinDbg, le code de l’indicateur est utilisé pour afficher ou modifier les indicateurs. L’indicateur status n’est pas pris en charge.

Voici un exemple. Dans l’affichage du registre précédent, l’indicateur status ng s’affiche. Cela signifie que l’indicateur de signe est actuellement défini sur 1. Pour modifier ce paramètre, utilisez la commande suivante :

r sf=0

Cela définit l’indicateur de signe sur zéro. Si vous effectuez un autre affichage de registre, le code ng status n’apparaît pas. Au lieu de cela, le code pl status s’affiche.

L’indicateur de signe, l’indicateur zéro et l’indicateur de transport sont les indicateurs les plus couramment utilisés.

Conditions

Une condition décrit l’état d’un ou de plusieurs indicateurs. Toutes les opérations conditionnelles sur le x86 sont exprimées en termes de conditions.

L’assembleur utilise une abréviation d’une ou deux lettres pour représenter une condition. Une condition peut être représentée par plusieurs abréviations. Par exemple, AE (« supérieur ou égal ») est la même condition que NB (« pas en dessous »). Le tableau suivant répertorie certaines conditions courantes et leur signification.

Nom de la condition Indicateurs Signification

Z

ZF=1

Le résultat de la dernière opération était égal à zéro.

NZ

ZF=0

Le résultat de la dernière opération n’était pas égal à zéro.

C

CF=1

La dernière opération a nécessité un transport ou un emprunt. (Pour les entiers non signés, cela indique un dépassement de capacité.)

NC

CF=0

La dernière opération ne nécessitait pas de transport ou d’emprunt. (Pour les entiers non signés, cela indique un dépassement de capacité.)

S

SF=1

Le résultat de la dernière opération a son bit élevé défini.

NS

SF=0

Le résultat de la dernière opération a son bit élevé clair.

O

OF=1

Lorsqu’elle est traitée comme une opération d’entier signé, la dernière opération a provoqué un dépassement de capacité ou un flux inférieur.

Non

OF=0

Lorsqu’elle est traitée comme une opération d’entier signé, la dernière opération n’a pas causé de dépassement de capacité ou de sous-flux.

Les conditions peuvent également être utilisées pour comparer deux valeurs. L’instruction cmp compare ses deux opérandes, puis définit les indicateurs comme s’ils sont soustraits un opérande de l’autre. Les conditions suivantes peuvent être utilisées pour case activée le résultat de cmpvalue1, value2.

Nom de la condition Indicateurs Signification après une opération CMP.

E

ZF=1

value1 == value2.

NE

ZF=0

value1 != value2.

GE NL

SF=OF

value1>= value2. Les valeurs sont traitées comme des entiers signés.

LE NG

ZF=1 ou SF !=OF

value1<= value2. Les valeurs sont traitées comme des entiers signés.

G NLE

ZF=0 et SF=OF

value1>value2. Les valeurs sont traitées comme des entiers signés.

L NGE

SF !=OF

value1<value2. Les valeurs sont traitées comme des entiers signés.

AE NB

CF=0

value1>= value2. Les valeurs sont traitées comme des entiers non signés.

BE NA

CF=1 ou ZF=1

value1<= value2. Les valeurs sont traitées comme des entiers non signés.

Un NBE

CF=0 et ZF=0

value1>value2. Les valeurs sont traitées comme des entiers non signés.

B NAE

CF=1

value1<value2. Les valeurs sont traitées comme des entiers non signés.

Les conditions sont généralement utilisées pour agir sur le résultat d’une instruction cmp ou de test . Par exemple,

cmp eax, 5
jz equal

compare le registre eax au nombre 5 en calculant l’expression (eax - 5) et en définissant des indicateurs en fonction du résultat. Si le résultat de la soustraction est égal à zéro, l’indicateur zr est défini et la condition jz est vraie afin que le saut soit effectué.

Types de données

  • octet : 8 bits

  • word : 16 bits

  • dword : 32 bits

  • qword : 64 bits (inclut des doubles à virgule flottante)

  • tword : 80 bits (inclut des doubles étendus à virgule flottante)

  • oword : 128 bits

Notation

Le tableau suivant indique la notation utilisée pour décrire les instructions du langage d’assembly.

Notation Signification

r, r1, r2...

Registres

m

Adresse mémoire (consultez la section Modes d’adressage suivants pour plus d’informations.)

#n

Constante immédiate

r/m

Inscrire ou mémoire

r/#n

Constante d’inscription ou immédiate

r/m/#n

Registre, mémoire ou constante immédiate

Cc

Code de condition répertorié dans la section Conditions précédente.

T

« B », « W » ou « D » (octet, mot ou dword)

accT

Accumulateur de taille T : al si T = « B », ax si T = « W », ou eax si T = « D »

Modes d’adressage

Il existe plusieurs modes d’adressage différents, mais ils prennent tous la forme T ptr [expr], où T est un type de données (voir la section Types de données précédente) et expr une expression impliquant des constantes et des registres.

La notation de la plupart des modes peut être déduite sans grande difficulté. Par exemple, BYTE PTR [esi+edx*8+3] signifie « prendre la valeur du registre esi , y ajouter huit fois la valeur du registre edx , ajouter trois, puis accéder à l’octet à l’adresse résultante ».

Pipelining

Le Pentium est à double émission, ce qui signifie qu’il peut effectuer jusqu’à deux actions en une seule horloge. Toutefois, les règles sur le moment où il est capable d’effectuer deux actions à la fois ( appelées appairage) sont très compliquées.

Étant donné que x86 est un processeur CISC, vous n’avez pas à vous soucier des emplacements de délai de saut.

Accès à la mémoire synchronisé

Les instructions de chargement, de modification et de stockage peuvent recevoir un préfixe de verrou , qui modifie l’instruction comme suit :

  1. Avant d’émettre l’instruction, le processeur vide toutes les opérations de mémoire en attente pour garantir la cohérence. Toutes les préversions de données sont abandonnées.

  2. Lors de l’émission de l’instruction, le processeur dispose d’un accès exclusif au bus. Cela garantit l’atomicité de l’opération de chargement/modification/stockage.

L’instruction xchg obéit automatiquement aux règles précédentes chaque fois qu’elle échange une valeur avec la mémoire.

Toutes les autres instructions sont non verrouillées par défaut.

Prédiction de saut

Des sauts inconditionnels sont prévus.

Les sauts conditionnels sont prédits pour être effectués ou non, selon qu’ils ont été effectués la dernière fois qu’ils ont été exécutés. La taille du cache pour l’enregistrement de l’historique des sauts est limitée.

Si le processeur n’a pas d’enregistrement indiquant si le saut conditionnel a été effectué ou non lors de sa dernière exécution, il prédit les sauts conditionnels vers l’arrière comme étant effectués et les sauts conditionnels vers l’avant comme non effectués.

Alignement

Le processeur x86 corrige automatiquement l’accès à la mémoire non aligné, avec une pénalité de performances. Aucune exception n’est levée.

Un accès à la mémoire est considéré comme aligné si l’adresse est un multiple entier de la taille de l’objet. Par exemple, tous les accès BYTE sont alignés (tout est un multiple entier de 1), les accès WORD aux adresses paires sont alignés et les adresses DWORD doivent être un multiple de 4 pour être alignées.

Le préfixe de verrou ne doit pas être utilisé pour les accès à la mémoire non alignés.