Architecture x86
Le processeur Intel x86 utilise l’architecture CISC (Complex Instruction Set Computer), 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 des instructions spéciales complexes prédomineront.
Le processeur x86 trace son héritage au moins jusqu’au 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 entiers non privilégiés suivants.
eax |
Accumulateur |
ebx |
Registre de base |
ecx |
Registre de compteurs |
edx |
Registre des données : peut être utilisé pour l’accès aux ports d’E/S et les fonctions arithmétiques |
Esi |
Registre d’index source |
Edi |
Registre d’index de destination |
ebp |
Registre de pointeur de base |
Esp |
Pointeur de pile |
Tous les registres entiers sont 32 bits. Toutefois, beaucoup d’entre eux ont des sous-inscriptions 16 bits ou 8 bits.
hache |
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 |
Bas 16 bits d’esp |
Al |
Faible 8 bits d’eax |
ah |
8 bits élevés d’ax |
bl |
Faible 8 bits d’ebx |
Bh |
8 bits élevés de bx |
Cl |
Faible 8 bits d’ecx |
Ch |
8 bits élevés de cx |
dl |
Faible 8 bits d’edx |
Dh |
8 bits élevés de dx |
L’exploitation 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é.
Quand vous utilisez le ? (É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, la commande r (@) n’est pas requise dans la commande r (Registers). Par exemple, r ax=5 est 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 les 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 préservation du 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 en fonction de la convention d’appel.
Le registre eax reçoit des valeurs de retour de fonction si le résultat est de 32 bits ou plus petit. Si le résultat est de 64 bits, le résultat est stocké dans la paire edx :eax .
Voici une 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 vers la 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 vers la gauche, le pointeur « this » est passé dans le registre ecx et l’appelé nettoie la pile.
COM (__stdcall pour les appels de méthode C++)
Les paramètres de fonction sont transmis sur la pile, poussés de droite à gauche, puis le pointeur « this » est envoyé (push) sur la pile, puis la fonction est appelée. L’appelé nettoie la pile.
__fastcall
Les deux premiers arguments DWORD-or-smaller sont passés dans les registres ecx et edx . Les paramètres restants sont transmis sur la pile, poussés vers la gauche. L’appelé nettoie la pile.
__cdecl
Les paramètres de fonction sont transmis sur la pile, poussés vers la 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 du débogueur des registres et des indicateurs
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 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 à bits uniques et d’une variété d’utilisations.
Le tableau suivant répertorie les indicateurs x86 :
Code d’indicateur | Nom de l’indicateur | Valeur | État de l’indicateur | Description |
---|---|---|---|---|
de | 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 | Indicateur zéro | 0 1 | nzzr | Non égal à 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é même |
cf | Indicateur de transport | 0 1 | ccny | Pas de transport - Porter |
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. Elle ne doit pas être utilisée par d’autres applications. | ||
iopl | Niveau de privilège d’E/S | Niveau de privilège d’E/S : 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. Elle ne doit pas être utilisée par les applications. |
Lorsque les registres sont affichés à la suite d’une commande dans la fenêtre Commande du débogueur, il s’agit de l’état de l’indicateur affiché. Toutefois, si vous souhaitez modifier un indicateur à l’aide de la commande r (Registers), vous devez vous y référer par le code de l’indicateur.
Dans la fenêtre Registres de WinDbg, le code d’indicateur est utilisé pour afficher ou modifier des indicateurs. L’état de l’indicateur n’est pas pris en charge.
Voici un exemple. Dans l’affichage du registre précédent, l’état de l’indicateur ng s’affiche. Cela signifie que l’indicateur de signe est actuellement défini sur 1. Pour modifier ce problème, utilisez la commande suivante :
r sf=0
Cela définit l’indicateur de signe sur zéro. Si vous effectuez un autre enregistrement, le code d’état ng n’apparaît pas. Au lieu de cela, le code d’état pl 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 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 ci-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’a pas été égal à zéro. |
C |
CF=1 |
La dernière opération exigeait 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 n’a pas besoin d’un transport ou d’un 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 jeu de bits élevé. |
NS |
SF=0 |
Le résultat de la dernière opération a son bit clair élevé. |
O |
OF=1 |
Lorsqu’elle est traitée comme une opération entière signée, la dernière opération a provoqué un dépassement de capacité ou un sous-flux. |
NO |
OF=0 |
Lorsqu’elle est traitée comme une opération entière signée, la dernière opération n’a pas déclenché 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 des indicateurs comme s’il était soustrait un opérande de l’autre. Les conditions suivantes peuvent être utilisées pour vérifier le résultat de la valeur cmp1, valeur2.
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 sera défini et la condition jz sera vraie afin que le saut soit pris.
Types de données
octets : 8 bits
word : 16 bits
dword : 32 bits
qword : 64 bits (inclut des doubles à virgule flottante)
deuxe : 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 de langage d’assembly.
Notation | Signification |
---|---|
r, r1, r2... |
Caisses |
m |
Adresse mémoire (consultez la section Modes d’adressage réussis pour plus d’informations.) |
#n |
Constante immédiate |
r/m |
Inscrire ou mémoire |
r/#n |
Inscrire ou immédiatement une constante |
r/m/#n |
Inscrire, mémoire ou constante immédiate |
cc |
Code de condition répertorié dans la section Conditions précédentes. |
T |
« B », « W » ou « D » (octet, mot ou mot) |
accT |
Taille T accumulateur : al if T = « B », ax if T = « W », or eax if 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édent) et expr est une expression impliquant des constantes et des registres.
La notation pour la plupart des modes peut être déduite sans beaucoup de difficulté. Par exemple, BYTE PTR [esi+edx*8+3] signifie « prendre la valeur du registre esi , l’ajouter huit fois la valeur du registre edx , ajouter trois, puis accéder à l’octet à l’adresse résultante ».
Pipelining
Le Pentium est double problème, ce qui signifie qu’il peut effectuer jusqu’à deux actions en une tique d’horloge. Toutefois, les règles sur le moment où il est capable d’effectuer deux actions à la fois (appelée 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ée
Les instructions de chargement, de modification et de magasin peuvent recevoir un préfixe de verrou , qui modifie l’instruction comme suit :
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érécupérations de données sont abandonnées.
Lors de l’émission de l’instruction, l’UC aura un accès exclusif au bus. Cela garantit l’atomicité de l’opération de chargement/modification/magasin.
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 par défaut non verrouillées.
Prédiction de saut
Les sauts inconditionnels sont prévus pour être pris.
Les sauts conditionnels sont prédits comme étant pris ou non, selon qu’ils ont été pris la dernière fois qu’ils ont été exécutés. Le cache de l’historique des sauts d’enregistrement est limité en taille.
Si le processeur n’a pas d’enregistrement indiquant si le saut conditionnel a été pris ou non la dernière fois qu’il a été exécuté, il prédit les sauts conditionnels vers l’arrière comme pris et les sauts conditionnels vers l’avant comme non pris.
Alignement
Le processeur x86 corrige automatiquement l’accès à la mémoire non alignée, à 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.