Partager via


Prologue et épilogue x64

Chaque fonction qui alloue de l’espace de pile, appelle d’autres fonctions, enregistre des registres nonvolatiles ou utilise une gestion des exceptions doit avoir un prolog dont les limites d’adresse sont décrites dans les données de déroulement associées à l’entrée de table de fonctions correspondante. Pour plus d’informations, consultez gestion des exceptions x64. Le prolog enregistre les registres d’arguments dans leurs adresses d’accueil si nécessaire, envoie (push) des registres nonvolatiles sur la pile, alloue la partie fixe de la pile pour les locaux et temporaires, et établit éventuellement un pointeur d’image. Les données de déroulement associées doivent décrire l’action du prologue et fournir les informations nécessaires pour annuler l’effet du code de prologue.

Si l’allocation fixe dans la pile est supérieure à une page (autrement dit, supérieure à 4096 octets), il est possible que l’allocation de pile puisse s’étendre sur plusieurs pages de mémoire virtuelle et, par conséquent, l’allocation doit être vérifiée avant son allocation. Une routine spéciale pouvant être appelée à partir du prologue et qui ne détruit aucun des registres d’arguments est fournie à cet effet.

La méthode recommandée pour enregistrer des registres nonvolatiles consiste à les déplacer vers la pile avant l’allocation de pile fixe. Si l’allocation de pile fixe est effectuée avant l’enregistrement des registres nonvolatiles, le déplacement 32 bits est probablement nécessaire pour traiter la zone de registre enregistrée. (Apparemment, les envois de registres sont aussi rapides que les mouvements et devraient rester ainsi pour l’avenir prévisible malgré la dépendance implicite entre les push.) Les registres nonvolatiles peuvent être enregistrés dans n’importe quel ordre. Toutefois, la première utilisation d’un registre nonvolatile dans le prologue doit être de l’enregistrer.

Code prolog

Le code d’un prologue classique peut être :

    mov    [RSP + 8], RCX
    push   R15
    push   R14
    push   R13
    sub    RSP, fixed-allocation-size
    lea    R13, 128[RSP]
    ...

Ce prolog stocke le registre d’arguments RCX dans son emplacement d’accueil, enregistre les registres nonvolatiles R13-R15, alloue la partie fixe du cadre de pile et établit un pointeur d’image qui pointe vers 128 octets dans la zone d’allocation fixe. L’utilisation d’un décalage permet à plusieurs zones d’allocation fixes d’être traitées avec des décalages d’un octet.

Si la taille d’allocation fixe est supérieure ou égale à une page de mémoire, une fonction d’assistance doit être appelée avant de modifier RSP. Cet assistance, , __chkstksonde la plage de pile à allouer pour s’assurer que la pile est étendue correctement. Dans ce cas, l’exemple de prologue précédent serait à la place :

    mov    [RSP + 8], RCX
    push   R15
    push   R14
    push   R13
    mov    RAX,  fixed-allocation-size
    call   __chkstk
    sub    RSP, RAX
    lea    R13, 128[RSP]
    ...

L’assistance __chkstk ne modifie pas les registres autres que R10, R11 et les codes de condition. En particulier, il retourne RAX inchangé et laisse tous les registres nonvolatiles et les registres de passage d’arguments non modifiés.

Code Epilog

Le code Epilog existe à chaque sortie d’une fonction. Alors qu’il n’y a normalement qu’un seul prologue, il peut y avoir beaucoup d’épilogues. Le code Epilog réduit la pile à sa taille d’allocation fixe (si nécessaire), libère l’allocation de pile fixe, restaure les registres nonvolatiles en dépilant leurs valeurs enregistrées à partir de la pile et en retourne.

Le code d’épilogie doit suivre un ensemble strict de règles pour le code de déroulement afin de déroutant de manière fiable les exceptions et les interruptions. Ces règles réduisent la quantité de données de déroulement requises, car aucune donnée supplémentaire n’est nécessaire pour décrire chaque épilogue. Au lieu de cela, le code de déroulement peut déterminer qu’un épilogue est exécuté en analysant vers l’avant via un flux de code pour identifier un épilogue.

Si aucun pointeur d’image n’est utilisé dans la fonction, l’épilogue doit d’abord libérer la partie fixe de la pile, les registres nonvolatiles sont dépilés et le contrôle est retourné à la fonction appelante. Par exemple,

    add      RSP, fixed-allocation-size
    pop      R13
    pop      R14
    pop      R15
    ret

Si un pointeur d’image est utilisé dans la fonction, la pile doit être réduite à son allocation fixe avant l’exécution de l’épilogue. Cette action ne fait pas partie techniquement de l’épilogue. Par exemple, l’épilogue suivant peut être utilisé pour annuler le prologue précédemment utilisé :

    lea      RSP, -128[R13]
    ; epilogue proper starts here
    add      RSP, fixed-allocation-size
    pop      R13
    pop      R14
    pop      R15
    ret

Dans la pratique, lorsqu’un pointeur d’image est utilisé, il n’existe aucune bonne raison d’ajuster RSP en deux étapes. Par conséquent, l’épilogue suivant sera utilisé à la place :

    lea      RSP, fixed-allocation-size - 128[R13]
    pop      R13
    pop      R14
    pop      R15
    ret

Ces formulaires sont les seuls à être juridiques pour un épilogue. Il doit se composer d’un add RSP,constant ou , suivi d’une série de zéro ou plus de 8 octets pops et a ou a jmpreturn lea RSP,constant[FPReg]. (Seul un sous-ensemble d’instructions jmp est autorisé dans l’épilogue. Le sous-ensemble est exclusivement la classe d’instructions jmp avec des références de mémoire ModRM où la valeur du champ mod ModRM est 00. L’utilisation d’instructions jmp dans l’épilogue avec la valeur du champ mod ModRM 01 ou 10 est interdite. Consultez le tableau A-15 dans le manuel manuel du programmeur d’architecture AMD x86-64 3 : Instructions générales sur l’usage général et le système, pour plus d’informations sur les références ModRM autorisées.) Aucun autre code ne peut apparaître. En particulier, rien ne peut être planifié dans un épilogue, y compris le chargement d’une valeur de retour.

Lorsqu’un pointeur d’image n’est pas utilisé, l’épilogue doit utiliser add RSP,constant pour libérer la partie fixe de la pile. Il peut ne pas être utilisé lea RSP,constant[RSP] à la place. Cette restriction existe afin que le code de déroulement ait moins de modèles à reconnaître lors de la recherche d’épilogues.

En suivant ces règles, le code de déroulement permet de déterminer qu’un épilogue est en cours d’exécution et de simuler l’exécution du reste de l’épilogue pour permettre de recréer le contexte de la fonction appelante.

Voir aussi

Conventions des logiciels x64