Retour sur la semaine précédente Types de données Rangement des données Modes d’adressage Gestion de la pile Variables locales Passage des arguments Interface avec les langages de haut niveau Récursivité, réentrance et translatabilité
La semaine précédente Compilateur Assembleur L’éditeur de liens « Listing files » Assembleur « inline » Déverminage Masm32 Programme complètement en assembleur Programme mixte
Types de données entières Octet (byte) Mot (word) Mot double (doubleword) BCD BCD compacté (packed BCD) Near Pointer Far Pointer (Logical Address) Champ de bits (bit field)
Types de données virgule flottante Simple précision Double précision Précision étendue Décimal compacté
float Simple précision Nombres normalisés e = 0 réservé pour 0 et les nombres dénormalisés e = 255 réservé pour les infinis et les NaNs
Normalisé vs dénormalisé Nombres normalisés (e = 1 à 254) Nombre maximum normalisé: 1,11111111111111111111111 2127 = 3,402823 1038 Nombre minimum normalisé: 1,00000000000000000000000 2-126 = 1,175494 10-38 Nombres dénormalisés (e = 0 et f ≠ 0) e = 0 et f ≠ 0. Alors: N = (-1)s 0,f 2-126 Nombre dénormalisé minimum: 0.00000000000000000000001 2-126 = 1,401298 10-45
Zéro, infini et NaN Zéro (e = 0 et f = 0) Zéro est représenté par 1,0000000000000000000000 x 2-127 = 00000000000000000000000000000000 Infini (e = 255 et f = 0) L’infini est représenté par 1,0000000000000000000000 x 2128 = 01111111100000000000000000000000 = 7F800000 Nan (e = 255 et f ≠ 0)
double Double précision Nombres normalisés: e = 1 à 2046 e = 0 réservé pour 0 et les nombres dénormalisés e = 2047 réservé pour les infinis et les NaNs
Limites des doubles Nombres normalisés (e entre 1 et 2026) Nombre maximum normalisé: 1,111111111111111… 21023 = 1,79769313486 10308 Nombre minimum normalisé: 1,000000000000000… 2-1022 = 2,22507385851 10-308 Nombres dénormalisés (e = 0 et f ≠ 0) e = 0 et f ≠ 0. Alors: N = (-1)s 0,f 2-1022 Nombre dénormalisé minimum: 0,00000000…000001 2-1022 = 4,94065645842 10-324
Zéro, Infini et NaN Zéro (e = 0 et f = 0) Zéro est représenté par 1,00000… x 2-1023 = 0000000000000000 Infini (e = 2047 et f = 0) L’infini est représenté par 1,00000000 x 21024 = 011111111111000000000…. = 7FF0 0000 0000 0000 Nan (e = 2047 et f ≠ 0)
Précision étendue Précision étendue Nombres normalisés: e = 1 à 32766 e = 0 réservé pour 0 et les nombres dénormalisés e = 32767 réservé pour les infinis et les NaNs
Limites Nombres normalisés (e entre 1 et 16383) Nombre maximum normalisé : 1,11111111111111111… 216383 = 6 104931 Nombre minimum normalisé : 1,000000……0000000... 2-16383 = 8 10-4933 Nombres dénormalisés e = 0 et f ≠ 0. Alors: N = (-1)s 0,f 2-16383 Nombre dénormalisé minimum: 0,00000000…000001 2-16383 = 9 2-4952
Format Packed Decimal Format Packed Decimal Représentation : -1018 + 1 à 1018 - 1
Les NaN Les NaN ne représentent pas des nombres, on ne peut donc pas les comparer avec des nombres. Ce sont des quantités considérées comme non ordonnées. • Pour la même raison, le signe des NaN est ignoré, donc un NaN n’est ni positif ni négatif. • La fraction d’un QNaN (Quiet NaN)est toujours de la forme : 0,1xxxxx…xxxxxxx tandis que celle d’un SNaN (Signaling NaN) est de la forme : 0,0xxxxx…xxxxxxx
Les NaN (la suite) Les Nan • Les SNaN signalent des exceptions lorsqu’ils sont fournis comme opérandes arithmétiques. Ils ne sont jamais générés par une opération. On peut toutefois les créer manuellement en mémoire. • Les QNaN représentent le résultat de certaines opérations invalides telles que des opérations arithmétiques invalides sur des infinis ou sur des NaN. • Un QNaN est généré dans les conditions suivantes: - Une opération invalide se produit alors que le masque d’opération invalide de FPSCR est 1.
- Par exemple ∞ - ∞, ∞ ÷ ∞, 0 ÷ 0, ∞ 0, opération sur un SNaN, comparaison ordonnée avec un NaN.
- La conversion en entier d’un infini ou d’un NaN.
Les NaN (la suite) Les Nan • Les QNaN se propagent à travers toutes les opérations, sauf les comparaisons ordonnées et la conversion en entier, sans signaler d’exception. • Le code d’un QNaN donné peut servir d’information de diagnostic pour permettre d’identifier les résultats d’opérations invalides. • Si l’un ou l’autre des opérandes d’une opération est un NaN, le résultat de l’opération est ce NaN. • Chacune des conditions pouvant provoquer un NaN peut générer une exception, c’est-à-dire une interruption logicielle, si une telle exception est autorisée dans le registre FPCR.
Type de données MMX Octets compactés Mots compactés Double mots compactés Quadruples mots
Types de données SIMD Doubles compactés Quadruples mots Simples compactés Doubles mots compactés Mots compactés Octets compactés
Rangement des données Little Endian vs Big Endian Chez Intel, l’octet de poids faible est stocké à l’adresse la plus basse (Little Endian).
Modes d’adressage Registre Immédiat Direct Implicite Indirection registre
Adressage de registre - mov ebx, eax ; (ebx) = (eax)
Le plus utilisé Très rapide Utilisé pour tout les calcule interne au processeur La largeur de la données dépends de la largeur du registre - AL 8 bits
- AX 16 bits
- EAX 32 bits
Adressage immédiat - mov eax, 0122Bh ; ou mov eax, 0x0122B
mov eax, -1 ; eax = 0xFFFFFFFF Utilisé lors de calcule impliquant des constantes La données et encodé à même l’instruction
Adressage direct variable est traduite par l’assembleur en offset par rapport au début du segment (habituellement le data segment). Cet offset est l’adresse à laquelle la variable a été implantée. Utilisé pour lire ou écrire des variables de type simple
Adressage implicite Certaines instructions n’ont pas d’opérande explicite et la description de l’adresse est contenue dans l’instruction elle-même ou dans des registres prédéfinis - ret ; dépile l’adresse de retour
- xlat ; utilise EBX et AL
- mul ; utilise edx et eax
- div ; utilise edx et eax
Exemple 01 ; Constantes ; //////////////////////////////////////////////////////// .const X dw 2 ; Variables ; //////////////////////////////////////////////////////// .data sZ dw ? ; Debut du segment de code ; ////////////////////////////////////////////////////////
Exemple 01 (suite) .code ; ===== Start ======================================= ; Le point d'entree du programme start: mov ax, X add ax, 2 mov sZ, ax push 0 call ExitProcess end start
Indirection registre - mov edx, [ebx] ; adresse dans ebx
Le registre entre crochets est appelé registre de base. Les registres suivants peuvent servir de registre de base : - eax edi
- ebx esi
- ecx ebp
- edx esp
Indirection registre avec offset - mov eax, [ebx + 8] ; adresse = ebx + 8
Exemple 02 Disponible sur le site du cours
Instruction lea lea Load Effective Address Calcule l’adresse efficace de l’opérande source et place le résultat dans le registre destination. - oszapc = oszapc
- lea reg, mem 0,5
- Équivalent :
- mov reg, offset mem 0,5
Quand doit-on utiliser lea au lieu de mov ? - Pour les paramètres d'une fonction, on utilise toujours mov.
- Pour les variables, on utilise lea lorsqu'il s'agit d'une variable de type tableau, mais on utilise mov lorsqu'il s'agit d'un pointeur ou d'un type simple.
Indirection registre avec offset registre L’offset registre est aussi appelé index - mov [ebx + edi * k],eax ; adresse = ebx + edi * k
; k = 1, 2, 4 ou 8 seulement Indirection registre avec index + offset - mov ax,[ebx + esi * k + 16] ; adresse= ebx + esi * k + 16
Les registres suivants peuvent servir d’index : - eax edi
- ebx esi
- ecx ebp
- edx
Utilisation des index Dans les modes d’adressage précédents, la constante k permet d’ajuster l’instruction à la taille de l’opérande, de façon à accéder directement au ième élément d’un tableau. Pour des octets (char, byte, boolean), k = 1, pour des mots (short), k = 2, pour des mots doubles (int, long, float), k = 4, pour des mots quadruples (long long, double), k = 8. .data tableau dw 50, 75, 342, -9, … .code lea ebx, tableau mov esi, i mov ax, [ebx + esi * 2] ; ax = tableau[ i ] ...
Exemple 03 Disponible sur le site du cours
Entrées-sorties en mode console Dans les exemples précédents, nous aurions pu afficher les résultats en utilisant la méthode démontré dans l’exemple 04 A ne surtout pas oublier ! Il faut compiler au moyen de Console Assemble & Link.
Programmation Windows On n’utilise plus la console dans les applications contemporaines, mais plutôt une fenêtre avec menus, boutons, bandes de défile-ment, case de fermeture, etc. Par exemple : Pour réaliser de telles applications, il faut faire appel aux libraires de Windows (API). La programmation Windows dépasse le cadre de ce cours, mais vous trouverez des exemples dans les travaux pratiques et sur le site du cours.
Instruction mov mov Move Data Copie l’opérande source dans l’opérande destination - oszapc = oszapc
- mov reg, reg mov dx, bx 0,5
- mem, reg mov table[edi], ebx
- reg, mem mov ebx, a
- reg, immed mov cx, 256
- mem, immed mov word ptr [ebx], 15
Instructions movzx et movsx Variantes de mov : movzx et movsx Supposons que bl contient 0x94 movzx ax, bl -> ax = 0094 = + 148 sur 16 bits movzx eax, bl -> eax = 00000094 = + 148 sur 32 bits movsx ax, bl -> ax = FF94 = –108 sur 16 bits movsx eax, bl -> eax = FFFFFF94 = – 108 sur 32 bits Ces instructions permettent de changer le type d’une variable sx = Signe extend zx = Zero extend
Instructions xchg, xlat et xlatb xchg Exchange Échange les deux opérandes oszapc = oszapc xchg reg, reg xchg cx, dx mem, reg xchg [ebx], ax reg, mem xchg ebx, pointer xlat, xlatb Translate Remplace un indice contenu dans al par le contenu correspondant d’une table dont l’adresse est dans ebx. oszapc = oszapc xlat mem xlatb
Pile d’exécution À quoi sert-elle ? Placer l’adresse de retour lors d’un appel de fonction Sauvegarder le contexte d’exécution lorsqu’une interruption doit être traité Placer les variables locales Passer les arguments à une fonction
Comment le Pentium IV manipule la pile ? SS Stack Segment ESP Stack Pointer EBP Base Pointer ESP pointe sur la dernière donnée empiler et la première à être dépilé.
Instruction call Exemples: call Fonction call sVariable Déroulement: Ajouter la grandeur de l’adresse de retour à ESP Placer l’adresse de retour dans l’espace mémoire pointé par ESP Placer l’opérande dans IP ou EIP
Instruction ret Exemples: ret ret 12 Déroulement: Lire l’adresse de retour de l’espace mémoire pointé par ESP et la placer dans IP ou EIP Ajouter la grandeur de l’adresse de retour à ESP Ajoute l’opérande à ESP Spécialement conçu pour qu’une fonction puisse enlever elle même ses arguments de la pile
Exemple 05 Disponible sur le site du cours
La pile et les interruptions Lors d’un interruption certaines informations supplémentaires sont placé sur la pile comparativement à un appel de fonction normal cs Code Segment eflags Registre de flags Dans certains cas, d’autres informations sont aussi sauvegardé Le retour d’un interruption se fait à l’aide de l’instruction iret et non ret
Instruction push Exemples: push eax push 64 Déroulement: Soustrait la grandeur de l’opérande à ESP Place l’opérande dans l’espace mémoire pointé par ESP Dans le mode 32 bit, tout les items poussés sur la pile occupent 4 octets.
Instruction pop Exemples: pop eax pop sVariable Déroulement: Lit la valeur pointée par ESP et la place dans l’opérande Ajoute la grandeur de l’opérande à ESP
pusha – pushad – popa – popad Ces quatre instructions manipule l’ensemble des registre généraux d’un seul coup Sans le D final, les registres suivants sont affectés AX, CX, DX, BX, SP, BP, SI, DI Avec le D final, les registres suivants sont affectés EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI Très utile pour sauvegarder le contexte d’exécution lors du traitement d’un interruption
pushf – pushfd – popf – popfd Cas particulier pour le traitement du registre de flag Si le processeur est dans un mode d’exécution qui ne permet pas le changement de certains bits du registre, ces bits ne change pas et aucune erreur ne se produit Vous pouvez utiliser ces deux instructions pour conserver, sur la pile, le résultat d’une comparaison
Variables locales Création sub esp, 12 Utilisation mov eax, [esp] ; Variable 3 inc [esp + 4] ; Variable 2 add eax, [esp + 8] ; Variable 1 Destruction add esp, 12
« Stack Frame » Espace de rangement pour les variables locales … Empiler EBP Copier ESP dans EBP Ajouter la grandeur des variables locales à ESP
Adressage des variables locales Habituellement les variables locales sont adressées en utilisant un adressage indirect basé sur le registre EBP Supposons que la première variable locale soit un mot double, nous pourrions lui ajouter un de la manière suivante inc [ebp+4]
Instruction enter Exemple: enter 12,0 Description: Création d’un « Stack Frame » avec N bytes de variables locales
Instruction leave Exemple: leave Description: Retirer un « Stack Frame » de la pile
Passage des arguments Par variables globales Par registres cdecl stdcall fastcall
Passage par variables globales Le moyen le plus simple de tous ! L’appelant place les arguments dans des variables globales connues de tous L’appelé lit les arguments de ces même variables globales La valeur de retour peut suivre le même chemin Voir l’exemple 06 Que faire pour les fonctions récursives ? Que faire pour les fonctions utilisés par plusieurs threads ?
Passage par registres La méthode plus rapide de tous Très simple Permet les fonctions réentrantes Il faut cependant oublier plusieurs des registres: cs, ds, es, ss, esp,… Il reste donc un nombre limité de registres La valeur de retour peut prendre le même chemin Oblige à sauvegarder les arguments en mémoire pour retrouver l’usage des registres nécessaires aux opérations
cdecl Les arguments sont empilés dans l’ordre inverse de leur déclaration L’appelant a la responsabilité de dépiler les arguments Les arguments plus petit qu’un mot double sont tout de même empilés sous la forme de mots doubles pour préserver l’alignement de la pile
La valeur de retour Si la valeur de retour est un mot double ou qu’elle est plus petite qu’un mot double, elle est placé dans eax Si c’est un mot quadruple, elle est placé dans la paire de registres edx:eax Si c’est une valeur virgule flottante, elle est placé dans st(0)
stdcall Les arguments sont empilés dans l’ordre inverse de leur déclaration La fonction appelée à la responsabilité de dépiler les arguments Si la fonction a un nombre variable d’argumente, c’est l’appelant qui a la responsabilité de dépiler les arguments Les arguments plus petit qu’un mot double sont tout de même empilés sous la forme de mots doubles pour préserver l’alignement de la pile
Fastcall Le premier argument est passé dans le registre ecx Le second argument est passé dans le registre edx Les autres sont empilés sur la pile dans l’ordre inverse de leur déclaration Les arguments plus petit qu’un mot double sont tout de même empilés sous la forme de mots doubles pour préserver l’alignement de la pile
Le “Stack Frame” et les arguments [ebp + 16] [ebp + 12] [ebp + 8] [ebp + 4] [ebp] [ebp – 4] [ebp – 8]
Récursivité Un programme récursif est un programme qui s’appelle lui-même. Un exemple bien connu est celui de la fonction factorielle, même si ce n’est pas une façon très efficace d’évaluer cette fonction: unsigned long factorial(unsigned long n) { if (n == 1) return 1; else return n * factorial(n - 1); }
Récursivité (la suite) En assembleur du Pentium, on aura: factorial: mov eax, [esp+4] cmp eax, 1 // if (n == 1) jne suite mov eax, 1 // return 1 jmp fin suite: dec eax push eax // factorial (n - 1) call factorial add esp, 4 // factorial (n-1) dans eax mov ebx, n mul ebx // n * factorial (n - 1) dans eax fin: ret
Réentrance Définition Un programme dit est réentrant s’il peut être interrompu et que le programme interrupteur peut l’appeler et obtenir un résultat correct, et qu’après l’interruption, le programme interrompu donne lui aussi un résultat correct; Conditions - Avoir du code pur, c.-à-d. qui ne se modifie pas lui-même: aucune information interne au code ne peut être modifiée par le code;
- Ne jamais utiliser de stockage temporaire interne au code;
- Le programme doit être interruptible.
Un programme récursif doit être rentrant (s’il n’interdit pas les interruptions), mais un programme réentrant n’est pas nécessairement récursif.
Translatabilité Un sous-programme est dit translatable ou indépendant de la position s'il fonctionne correctement où qu'il soit placé en mémoire. Un programme indépendant de la position ne nécessite pas un chargeur relocalisant car toutes les adresses sont exprimées par rapport à la valeur courante du compteur ordinal (EIP). Il peut ainsi être utilisé avec n'importe quelle combinaison d'autres programmes. Un programme indépendant de la position est donc presque toujours préférable à une programme qui ne l'est pas, même s'il est de 5 à 10% moins rapide.
Comment faire Pour écrire du code indépendant de la position, il faut: - a. Éviter les branchements absolus (Ex. jmp 0x1000)
- b. Référer aux variables au moyen des modes d'adressage indexés qui utilisent un offset par rapport à un registre comme ebp ou esp.
- c. Utiliser la pile système pour du stockage temporaire (allocation dynamique). On peut par exemple réserver 32 octets dans la pile en soustrayant 32 du pointeur de pile avec l'instruction
sub esp, 32 - On réfère ensuite à cet espace mémoire avec des offsets indexés par rapport à esp ou ebp.
Déterminer l’adresse d’exécution Si nécessaire, on peut toujours déterminer la valeur courante du compteur ordinal au moyen d'une instruction comme : ici: lea eax, ici Le programme peut donc calculer son adresse réelle en mémoire.
Translatabilité et adresses absolues Il existe habituellement des adresses absolues dans une machine, par exemple, celles des vecteurs d ’interruption. On peut accéder à de telles adresses de façon absolue sans nuire à la translatabilité du programme, si elles ne se trouvent pas dans la zone d'implantation du programme.
Do'stlaringiz bilan baham: |