Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Vue d’ensemble de la gestion structurée des exceptions et des conventions de codage de gestion des exceptions C++ sur le x64. Pour obtenir des informations générales sur la gestion des exceptions, consultez Gestion des exceptions dans Microsoft C++.
Décompresser les données pour la gestion des exceptions et la prise en charge du débogueur
Pour récupérer des registres nonvolatiles lorsqu’une exception est gérée, les fonctions non actives sont annotées avec des données statiques. Ces données, communément appelées « informations de déroulement d’une fonction », décrivent comment dérouler correctement la fonction à partir de n’importe quelle instruction. Ces données sont stockées sous forme de données pdata ou de procédure, qui à leur tour font référence à xdata, aux données de gestion des exceptions.
Les informations de déroulement de la fonction se composent de plusieurs structures de données, décrites ci-après.
Pour les informations Unwind prenant en charge Intel APX (Advanced Performance Extensions), consultez la spécification préliminaire Unwind V3.
structure RUNTIME_FUNCTION
La gestion des exceptions basée sur une table nécessite une entrée de table pour toutes les fonctions qui allouent de l’espace de pile ou appellent une autre fonction (par exemple, les fonctions non-en-feuilles). Les entrées de table de fonctions ont le format suivant :
| Taille | Valeur |
|---|---|
| ULONG | Adresse de début de la fonction |
| ULONG | Adresse de fin de fonction |
| ULONG | Adresse d'informations de débobinage |
La RUNTIME_FUNCTION structure doit être DWORD alignée en mémoire. Toutes les adresses sont relatives à l’image, c’est-à-dire qu’elles sont des décalages 32 bits par rapport à l’adresse de départ de l’image qui contient l’entrée de la table de fonctions. Ces entrées sont triées et placées dans la .pdata section d’une image PE32+. Pour les fonctions générées dynamiquement [compilateurs JIT], le runtime pour prendre en charge ces fonctions doit utiliser RtlInstallFunctionTableCallback ou RtlAddFunctionTable fournir ces informations au système d’exploitation. L’échec de ce processus entraîne une gestion et un débogage d’exceptions non fiables.
struct UNWIND_INFO
La structure d’informations de déroulement enregistre les effets d’une fonction sur le pointeur de pile et l’emplacement où les registres non volatils sont sauvegardés dans la pile :
| Taille | Valeur |
|---|---|
| UBYTE : 3 | Version |
| UBYTE : 5 | Drapeaux |
| UBYTE | Taille du prologue |
| UBYTE | Nombre de codes de déroulement |
| UBYTE : 4 | Registre d’images |
| UBYTE : 4 | Décalage de registre de trame (mis à l’échelle) |
| USHORT * n | Tableau de codes de décodage |
| variable | Peut être de forme (1) ou (2) ci-dessous |
(1) Gestionnaire d’exceptions
| Taille | Valeur |
|---|---|
| ULONG | Adresse du gestionnaire d’exceptions |
| variable | Données de gestionnaire spécifiques au langage (facultatif) |
(2) Informations de déroulement chaînées
| Taille | Valeur |
|---|---|
| ULONG | Adresse de début de la fonction |
| ULONG | Adresse de fin de fonction |
| ULONG | Adresse d'informations de débobinage |
La structure UNWIND_INFO doit être alignée sur DWORD en mémoire. Voici ce que signifie chaque champ :
Version
Numéro de version des données de désempilage, actuellement 1.
Drapeaux
Trois indicateurs sont actuellement définis :
Indicateur Descriptif UNW_FLAG_EHANDLERLa fonction a un gestionnaire d’exceptions que le système d’exploitation appelle pour examiner l’état de l’exception et le gérer potentiellement. Les fonctionnalités du langage, telles que la clause C __try, permettent d’enregistrer un tel gestionnaire.UNW_FLAG_UHANDLERLa fonction possède une routine de terminaison que le système d’exploitation appelle pendant le dépilage de la pile. Ce gestionnaire peut libérer des ressources allouées par la fonction dans le code sécurisé de l’exception. Les fonctionnalités de langage telles que les destructeurs d’objets C++ locaux et les clauses C __finallyinscrivent un tel gestionnaire d’arrêt.UNW_FLAG_CHAININFOCette structure d’informations de déroulement n’est pas la principale pour la procédure. En revanche, l’entrée d’informations de déroulement chaînées correspond au contenu d’une entrée RUNTIME_FUNCTIONprécédente. Pour plus d'informations, consultez structures d'informations de désenroulement chaîné. Si ce drapeau est activé, les drapeauxUNW_FLAG_EHANDLERetUNW_FLAG_UHANDLERdoivent être désactivés. En outre, le registre de trame et les champs d’allocation de pile fixe doivent avoir les mêmes valeurs que dans les informations de débobinage principales.Taille du prologue
Longueur du prolog de fonction en octets.
Nombre de codes de déroulement
Nombre de cases dans le tableau de codes de déroulement. Certains codes de déroulement, tels que
UWOP_SAVE_NONVOL, nécessitent plusieurs emplacements dans le tableau.Registre de trames
Si elle n’est pas différente de zéro, la fonction utilise un pointeur d’image (FP), et ce champ est le nombre du registre nonvolatile utilisé comme pointeur d’image, en utilisant le même encodage pour le champ d’informations d’opération des
UNWIND_CODEnœuds.Décalage du registre de trame (mis à l’échelle)
Ce champ est un décalage mis à l’échelle entre la valeur du registre
RSPet la valeur du registre de pointeur de trame (FP) sélectionné. Le registre FP sélectionné est défini surRSP+ 16 * ce nombre, ce qui signifie que vous pouvez utiliser des décalages de 0 à 240. Ce décalage place le registre FP au milieu de la zone de pile locale allouée pour les cadres de pile dynamiques, ce qui permet d’obtenir une meilleure densité de code grâce à des instructions plus courtes. (Autrement dit, d’autres instructions peuvent utiliser le formulaire offset signé 8 bits.)Tableau de codes de déséroulement
Tableau d’éléments décrivant l’effet du prologue sur les registres non volatils et
RSP. Consultez la section consacrée aux codes des opérations de déroulement pour connaître la signification de chaque élément. Pour maintenir l’alignement des données approprié, ce tableau contient toujours un nombre pair d’entrées et l’entrée finale peut être inutilisée. Dans ce cas, le tableau est plus long que ce qui est indiqué par le nombre de codes de déroulement.Adresse du gestionnaire d’exceptions
Pointeur relatif à l’image vers le gestionnaire d’exception ou de terminaison spécifique à la langue de la fonction, si l’indicateur
UNW_FLAG_CHAININFOest clair et l’un des indicateursUNW_FLAG_EHANDLERouUNW_FLAG_UHANDLERest défini.Données de gestionnaire spécifiques à la langue
Données du gestionnaire d’exceptions propres au langage de la fonction. Le format de ces données n’est pas spécifié et entièrement déterminé par le gestionnaire d’exceptions spécifique en cours d’utilisation.
Informations de désenroulement chaîné
Si l’indicateur
UNW_FLAG_CHAININFOest défini, laUNWIND_INFOstructure se termine par troisUWORDs. CesUWORDreprésentent les informationsRUNTIME_FUNCTIONrelatives à la fonction de déroulement chaîné.
struct UNWIND_CODE
Utilisez le tableau des codes de déroulement pour enregistrer la séquence d’opérations effectuées dans le prologue et qui affectent les registres non volatils et RSP. Chaque élément de code a ce format :
| Taille | Valeur |
|---|---|
| UBYTE | Décalage dans le prologue |
| UBYTE : 4 | Déroulage du code d’opération |
| UBYTE : 4 | Informations sur l’opération |
Le tableau est trié par ordre décroissant de décalage dans le prolog.
Décalage dans le prologue
Décalage (à partir du début du prologue) de la fin de l'instruction qui effectue cette opération, plus 1 (c’est-à-dire, décalage du début de l'instruction suivante).
Déroulage du code d’opération
Certains codes d’opération nécessitent un décalage non signé par rapport à une valeur dans la trame de pile locale. Le décalage est mesuré à partir du début, c’est-à-dire à l’adresse la plus basse de la pile allouée de manière fixe. Si le champ Registre de trame dans le UNWIND_INFO vaut zéro, ce décalage est calculé à partir de RSP. Si le champ Registre de trame est non nul, ce décalage est calculé à partir de l’emplacement où RSP se trouvait lorsque le registre FP a été établi. Il est égal au registre FP moins le décalage du registre FP (16 * le décalage mis à l’échelle du registre de trame dans le UNWIND_INFO). Si un registre FP est utilisé, tout code de déroulement prenant un décalage ne doit être utilisé qu’après l’établissement du registre FP dans le prolog.
Pour tous les opcodes sauf UWOP_SAVE_XMM128 et UWOP_SAVE_XMM128_FAR, le décalage est toujours un multiple de 8, car toutes les valeurs de pile d’intérêt sont stockées sur des limites de 8 octets (la pile elle-même est toujours alignée sur 16 octets). Pour les codes d’opération qui prennent un décalage court (inférieur à 512 Ko), le final USHORT dans les nœuds de ce code contient le décalage divisé par 8. Pour les codes d’opération qui utilisent un décalage long (512 Ko <= décalage < 4 Go), les deux derniers nœuds USHORT de ce code contiennent le décalage (au format little-endian).
Pour les opcodes UWOP_SAVE_XMM128 et UWOP_SAVE_XMM128_FAR, le décalage est toujours un multiple de 16, car toutes les opérations 128 bits XMM doivent se produire sur la mémoire alignée sur 16 octets. Par conséquent, un facteur d'échelle de 16 est utilisé pour UWOP_SAVE_XMM128 permettre des décalages inférieurs à 1 Mo.
Le code d’opération de déroulement est l’une des valeurs suivantes :
UWOP_PUSH_NONVOL(0) 1 nœudEnvoyez un registre entier nonvolatile, décrémentant
RSPpar 8. Les données d'opération correspondent au numéro du registre. En raison des contraintes sur les épilogues,UWOP_PUSH_NONVOLles codes de déroulement doivent apparaître en premier dans le prologue et en conséquence, en dernier dans le tableau de codes de déroulement. Cet ordre relatif s’applique à tous les autres codes de déroulement, saufUWOP_PUSH_MACHFRAME.UWOP_ALLOC_LARGE(1) 2 ou 3 nœudsAllouez une zone de grande taille sur la pile. Deux formulaires sont disponibles. Si les informations d’opération sont égales à 0, la taille de l’allocation divisée par 8 est enregistrée dans l’emplacement suivant, ce qui permet une allocation allant jusqu’à 512 Ko - 8. Si l'information d’opération est égale à 1, la taille non mise à l’échelle de l’allocation est enregistrée dans les deux emplacements suivants au format little-endian, ce qui permet d’allouer jusqu’à 4 Go moins 8 octets.
UWOP_ALLOC_SMALL(2) 1 nœudAllouez une zone de petite taille sur la pile. La taille de l’allocation est le champ d’informations sur l’opération * 8 + 8, ce qui permet d’allouer de 8 à 128 octets.
Le code de déroulement d’une allocation de pile doit toujours utiliser l’encodage le plus court possible :
Taille d’allocation Code de désenroulement 8 à 128 octets UWOP_ALLOC_SMALL136 à 512 Ko-8 octets UWOP_ALLOC_LARGE, informations sur l’opération = 0512 Ko à 4G-8 octets UWOP_ALLOC_LARGE, informations sur l’opération = 1UWOP_SET_FPREG(3) 1 nœudÉtablissez le registre du pointeur de trame en définissant le registre sur un certain décalage par rapport à la valeur actuelle de
RSP. Le décalage est égal au champUNWIND_INFOde décalage du registre de trame (mis à l’échelle), multiplié par 16, ce qui permet des décalages de 0 à 240. L’utilisation d’un décalage permet d’établir un pointeur de cadre qui pointe vers le milieu de l’allocation de pile fixe, ce qui améliore la densité du code en permettant à davantage d'accès d'utiliser des formes d'instructions courtes. Le champ d’informations sur l’opération est réservé et ne doit pas être utilisé.UWOP_SAVE_NONVOL(4) 2 nœudsEnregistrez un registre entier nonvolatile sur la pile à l’aide d’un MOV au lieu d’un push. Ce code est principalement utilisé pour le shrink-wrapping, où un registre non volatil est sauvegardé dans la pile à une position précédemment allouée. Les données d'opération correspondent au numéro du registre. Le décalage de pile mis à l’échelle par 8 est enregistré dans l’emplacement de code de l’opération de déroulement suivant, comme décrit dans la remarque ci-dessus.
UWOP_SAVE_NONVOL_FAR(5) 3 nœudsEnregistrez un registre entier nonvolatile sur la pile avec un décalage long, à l’aide d’un MOV au lieu d’un push. Ce code est principalement utilisé pour le shrink-wrapping, où un registre non volatil est sauvegardé dans la pile à une position précédemment allouée. Les données d'opération correspondent au numéro du registre. Le décalage de pile non mis à l’échelle est enregistré dans les deux emplacements de code d’opération de déroulement suivants, comme décrit dans la remarque ci-dessus.
UWOP_SAVE_XMM128(8) 2 nœudsEnregistrez tous les 128 bits d’un registre nonvolatile
XMMsur la pile. Les données d'opération correspondent au numéro du registre. Le décalage de pile à une échelle de 16 est enregistré dans l’emplacement suivant.UWOP_SAVE_XMM128_FAR(9) 3 nœudsEnregistrez tous les 128 bits d’un registre nonvolatile
XMMsur la pile avec un décalage long. Les données d'opération correspondent au numéro du registre. Le décalage de pile non mis à l’échelle est enregistré dans les deux emplacements suivants.UWOP_PUSH_MACHFRAME(10) 1 nœudPoussez un châssis de machine. Ce code de déroulement enregistre l’effet d’une interruption ou d’une exception matérielle. Il a deux formes. La valeur 0 indique que le matériel a envoyé (push) une trame telle que celle-ci sur la pile :
Emplacement Valeur RSP+32SSRSP+24Vieux RSPRSP+16EFLAGSRSP+8CSRSPRIPUne valeur de 1 indique que le matériel a empilé une trame telle que celle-ci sur la pile :
Emplacement Valeur RSP+40SSRSP+32Vieux RSPRSP+24EFLAGSRSP+16CSRSP+8RIPRSPCode d’erreur Ce code de désempilage se trouve toujours dans un prolog factice, qui n'est jamais réellement exécuté, mais apparaît avant le point d’entrée principal d'une routine d'interruption, et existe uniquement pour fournir un emplacement simulant la poussée d'une trame machine.
UWOP_PUSH_MACHFRAMEenregistre cette simulation, qui indique que la machine a effectué cette opération conceptuellement :Adresse de retour pop
RIPdu haut de la pile dans TempPousser
SSPousser l’ancien
RSPPousser
EFLAGSAppuyez sur
CSAppuyer sur Temp
Code d'erreur push (si op info est égal à 1)
L’opération simulée
UWOP_PUSH_MACHFRAMEdécrémenteRSPde 40 (si les informations d’opération sont égales à 0) ou 48 (si les informations d’opération sont égales à 1).
Informations sur l’opération
La signification des bits d’informations d’opération dépend du code d’opération. Pour encoder un registre à usage général (entier), ce mappage est utilisé :
| bit | S'inscrire |
|---|---|
| 0 | RAX |
| 1 | RCX |
| 2 | RDX |
| 3 | RBX |
| 4 | RSP |
| 5 | RBP |
| 6 | RSI |
| 7 | RDI |
| 8 à 15 |
R8 à R15. |
Structures d'informations de débobinage chaînées
Si l’indicateur UNW_FLAG_CHAININFO est défini, alors la structure d’informations de déroulement est une structure secondaire, et le champ d’adresse partagé du gestionnaire d’exception/des informations chaînées contient les informations de déroulement primaires. Cet exemple de code récupère les informations de déroulement principales, en supposant que unwindInfo est la structure pour laquelle l’indicateur UNW_FLAG_CHAININFO est activé.
PRUNTIME_FUNCTION primaryUwindInfo = (PRUNTIME_FUNCTION)&(unwindInfo->UnwindCode[( unwindInfo->CountOfCodes + 1 ) & ~1]);
Les informations enchaînées sont utiles dans deux situations. Tout d’abord, il peut être utilisé pour les segments de code noncontigues. En utilisant des informations chaînées, vous pouvez réduire la taille des informations de désempilage nécessaires, car vous n’avez pas besoin de dupliquer le tableau des codes de désempilage contenu dans les informations de désempilage principales.
Vous pouvez également utiliser des informations chaînées pour regrouper les sauvegardes de registres volatiles. Le compilateur peut retarder l’enregistrement de certains registres volatiles jusqu’à ce qu’il soit en dehors du prolog d’entrée de fonction. Vous pouvez les consigner en utilisant des informations de déroulement principales pour la partie de la fonction qui précède le code regroupé, puis en configurant des informations chaînées avec une taille de prologue non nulle, où les codes de déroulement dans les informations chaînées reflètent les sauvegardes des registres non volatils. Dans ce cas, les codes de désempilement sont tous de type UWOP_SAVE_NONVOL. Un groupe qui sauvegarde des registres non volatils en utilisant un PUSH, ou qui modifie le registre RSP au moyen d’une allocation fixe supplémentaire sur la pile, n’est pas pris en charge.
Un UNWIND_INFO élément pour lequel UNW_FLAG_CHAININFO est défini peut contenir une entrée RUNTIME_FUNCTION dont l’élément UNWIND_INFO a également UNW_FLAG_CHAININFO défini, parfois appelé encapsulage rétractable multiple. À terme, les pointeurs chaînés d’informations de déroulement aboutissent à un élément UNWIND_INFO pour lequel UNW_FLAG_CHAININFO est désactivé. Cet élément est l’élément principal UNWIND_INFO , qui pointe vers le point d’entrée de procédure réel.
Procédure de déroulement
Le tableau de codes de désenroulement est trié par ordre décroissant. Lorsqu’une exception se produit, le système d’exploitation stocke le contexte complet dans un enregistrement de contexte. La logique de répartition des exceptions est ensuite appelée, qui exécute à plusieurs reprises ces étapes pour rechercher un gestionnaire d’exceptions :
Utilisez la valeur actuelle de
RIPstockée dans l’enregistrement de contexte pour rechercher une entrée de tableRUNTIME_FUNCTIONqui décrit la fonction en cours (ou une partie de la fonction, pour les entréesUNWIND_INFOchaînées).Si la recherche ne trouve pas d’entrée dans la table des fonctions, le code est considéré comme faisant partie d’une fonction feuille, et
RSPadresse directement le pointeur de retour. Le pointeur de retour situé à l’adresse [RSP] est stocké dans le contexte mis à jour, leRSPsimulé est incrémenté de 8, et l’étape 1 est répétée.Si la recherche trouve une entrée de table de fonctions,
RIPpeut se trouver dans trois régions : a) dans un épilogue, b) dans le prolog ou c) dans le code qui peut être couvert par un gestionnaire d’exceptions.Cas a) Si le
RIPcontenu se trouve dans un épilogue, le contrôle quitte la fonction. Il ne peut y avoir aucun gestionnaire d’exceptions associé à cette exception pour cette fonction. Les effets de l’épilogue doivent continuer de prendre en compte le contexte de la fonction appelante. Pour déterminer si leRIPse trouve dans un épilogue, le flux de code à partir deRIPest examiné. Si ce flux de code correspond à la partie de fin d’une épilogie légitime, il se trouve dans un épilogue. La partie restante de l’épilogue est simulée, avec l’enregistrement de contexte mis à jour à mesure que chaque instruction est traitée. Après ce traitement, l’étape 1 est répétée.- Cas b) Si le
RIPse trouve dans le prologue, le contrôle n'est pas encore entré dans la fonction. Il ne peut y avoir aucun gestionnaire d’exceptions associé à cette exception pour cette fonction. Les effets du prologue doivent être annulés pour calculer le contexte de la fonction appelante. LeRIPest situé dans le prologue si la distance entre le début de la fonction et leRIPest inférieure ou égale à la taille du prologue encodée dans les informations de déroulement. Le mécanisme de déroulement parcourt vers l’avant le tableau des codes de déroulement jusqu’à la première entrée dont le décalage est inférieur ou égal au décalage deRIPà partir du début de la fonction, puis annule les effets de tous les éléments restants du tableau des codes de déroulement. L’étape 1 est ensuite répétée.
- Cas b) Si le
Cas c) Si le
RIPne se trouve pas dans un prologue ou un épilogue, et que la fonction possède un gestionnaire d’exceptions (UNW_FLAG_EHANDLERest défini), le gestionnaire spécifique au langage est appelé. Le gestionnaire analyse ses données et appelle les fonctions de filtre selon les besoins. Le gestionnaire spécifique à la langue peut retourner que l’exception a été gérée ou que la recherche doit être poursuivie. Il peut également initier un déroulement directement.
Si le gestionnaire spécifique à la langue retourne un état géré, l’exécution continue à l’aide de l’enregistrement de contexte d’origine.
S’il n’existe aucun gestionnaire spécifique à la langue ou si le gestionnaire renvoie un état « poursuivre la recherche », l’enregistrement du contexte doit être rétabli dans l’état de l’appelant. Le mécanisme de déroulement inverse l’effet de chaque élément du tableau des codes de déroulement. L’étape 1 est ensuite répétée.
Lorsque des informations de déroulement chaînées sont en jeu, ces étapes fondamentales sont toujours suivies. La seule différence est que, lorsqu’on parcourt le tableau des codes de déroulement pour annuler les effets d’un prologue, une fois arrivé à la fin du tableau, le processus établit un lien vers les informations de déroulement parentes et parcourt l’intégralité du tableau des codes de déroulement qui s’y trouve. Cette liaison continue jusqu’à ce qu’elle arrive à une information de déroulement sans l’indicateur UNW_CHAINED_INFO , puis elle termine la marche à pied de son tableau de code de déroulement.
Le plus petit ensemble de données de déroulement est de 8 octets. Un tel ensemble correspond à une fonction qui n’a alloué que 128 octets de pile ou moins, et a éventuellement sauvegardé un registre non volatile. Il s’agit également de la taille d’une structure d’informations de déroulement chaînée pour un prolog de longueur nulle sans codes de déroulement.
Gestionnaire spécifique à la langue
La UNWIND_INFO structure fournit l’adresse relative du gestionnaire spécifique à la langue lorsque UNW_FLAG_EHANDLER l’un ou UNW_FLAG_UHANDLER l’autre des indicateurs sont définis. Comme indiqué dans la section précédente, la recherche d’un gestionnaire d’exception ou le processus de désempilement appelle le gestionnaire propre au langage. Le gestionnaire utilise ce prototype :
typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE) (
IN PEXCEPTION_RECORD ExceptionRecord,
IN ULONG64 EstablisherFrame,
IN OUT PCONTEXT ContextRecord,
IN OUT PDISPATCHER_CONTEXT DispatcherContext
);
ExceptionRecord fournit un pointeur vers un enregistrement d’exception, qui a la définition Win64 standard.
EstablisherFrame est l'adresse de la base de l'allocation fixe de pile pour cette fonction.
ContextRecord pointe vers le contexte d’exception au moment où l’exception a été levée (dans le cas du gestionnaire d’exceptions) ou le contexte actuel de « déroulement » (dans le cas du gestionnaire de terminaison).
DispatcherContext pointe vers le contexte du répartiteur pour cette fonction. Elle a cette définition :
typedef struct _DISPATCHER_CONTEXT {
ULONG64 ControlPc;
ULONG64 ImageBase;
PRUNTIME_FUNCTION FunctionEntry;
ULONG64 EstablisherFrame;
ULONG64 TargetIp;
PCONTEXT ContextRecord;
PEXCEPTION_ROUTINE LanguageHandler;
PVOID HandlerData;
} DISPATCHER_CONTEXT, *PDISPATCHER_CONTEXT;
ControlPc est la valeur de RIP dans cette fonction. Cette valeur est une adresse d’exception ou l’adresse à laquelle le contrôle a été transféré de la fonction d'initialisation. Le RIP est utilisé pour déterminer si l’exécution se trouve dans une structure protégée à l’intérieur de cette fonction, par exemple, un bloc __try pour __try/__except ou __try/__finally.
ImageBase est la base d’images (adresse de chargement) du module contenant cette fonction. Les décalages 32 bits utilisés dans l’entrée de fonction et les données de déroulement doivent être ajoutés à ImageBase afin d’obtenir l’adresse finale.
FunctionEntry fournit un pointeur vers l’entrée de fonction RUNTIME_FUNCTION contenant les adresses, relatives à la base de l’image, de la fonction et des informations de déroulement pour cette fonction.
EstablisherFrame est l'adresse de la base de l'allocation fixe de pile pour cette fonction.
TargetIp fournit une adresse d’instruction facultative qui spécifie l’adresse de reprise du déroulement. Cette adresse est ignorée si EstablisherFrame n’est pas spécifié.
ContextRecord indique le contexte d'exception, utilisé par le système pour le code de répartition et de déroulement des exceptions.
LanguageHandler pointe vers la routine de gestionnaire de langage spécifique à la langue appelée.
HandlerData pointe vers les données de gestionnaire spécifiques au langage pour cette fonction.
Décompresser les helpers pour MASM
Pour écrire des routines d’assembly appropriées, utilisez un ensemble de pseudo-opérations en même temps que les instructions d’assembly réelles. Ces pseudo-opérations créent les éléments appropriés .pdata et .xdata. Utilisez également un ensemble de macros qui simplifient l’utilisation de ces pseudo-opérations pour leurs utilisations les plus courantes.
Pseudo-opérations brutes
| Pseudo-opération | Descriptif |
|---|---|
| PROC FRAME [ :ehandler] | Provoque la génération d’une entrée de table de fonctions dans .pdata et un déroulement des informations .xdata pour le comportement structuré de gestion des exceptions d’une fonction. Si le gestionnaire est présent, ce processus est entré dans .xdata en tant que gestionnaire spécifique au langage.Lorsque vous utilisez l’attribut FRAME, faites-le suivre de la directive .ENDPROLOG. Si la fonction est une fonction feuille (telle que définie dans les types de fonctions), l’attribut FRAME n’est pas nécessaire, comme le reste de ces pseudo-opérations. |
| .PUSHREG registre | Génère une entrée UWOP_PUSH_NONVOL de code de désenroulement pour le numéro du registre spécifié en utilisant le décalage actuel dans le prologue.Utilisez-le uniquement avec des registres entiers nonvolatiles. Pour les envois de registres volatiles, utilisez un . ALLOCSTACK 8, à la place. |
| .SETFRAME Registre, décalage | Renseigne le champ du registre de cadre et le décalage dans les informations de déroulement à l'aide du registre et du décalage spécifiés. Le décalage doit être un multiple de 16 et inférieur ou égal à 240. Cette directive génère également une entrée de code de désempilement UWOP_SET_FPREG pour le registre spécifié en utilisant le décalage actuel du prologue. |
| .ALLOCSTACK taille | Génère un UWOP_ALLOC_SMALL ou un UWOP_ALLOC_LARGE de la taille spécifiée pour l’offset actuel dans le prologue.L’opérande de taille doit être un multiple de 8. |
| .SAVEREG registre, décalage | Génère soit une entrée de code de déroulement UWOP_SAVE_NONVOL, soit une entrée UWOP_SAVE_NONVOL_FAR pour le registre et le décalage spécifiés en utilisant le décalage de prologue actuel. MASM choisit l’encodage le plus efficace.le décalage doit être positif et un multiple de 8. offset est relatif à la base de l’image de la procédure, qui est généralement dans RSP, ou, si vous utilisez un pointeur d’image, le pointeur d’image non mis à l’échelle. |
| .SAVEXMM128 registre, offset | Génère soit une entrée de code de désenroulement UWOP_SAVE_XMM128, soit une entrée de code de désenroulement UWOP_SAVE_XMM128_FAR pour le registre XMM et le décalage spécifiés, à l’aide du décalage de prologue actuel. MASM choisit l’encodage le plus efficace.le décalage doit être positif et un multiple de 16. offset est relatif à la base du cadre de la procédure, qui se trouve généralement dans RSP, ou, si un pointeur de cadre est utilisé, dans le pointeur de cadre sans mise à l’échelle. |
| . PUSHFRAME [code] | Génère une entrée de code de déroulage UWOP_PUSH_MACHFRAME. Si vous spécifiez le code facultatif, l’entrée de code de déroulement obtient un modificateur de 1. Sinon, le modificateur est 0. |
| . ENDPROLOG | Signale la fin des déclarations de prologue. Doit se produire dans les 255 premiers octets de la fonction. |
Voici un exemple de préambule de fonction avec une utilisation appropriée de la plupart des opcodes :
sample PROC FRAME
db 048h; emit a REX prefix, to enable hot-patching
push rbp
.pushreg rbp
sub rsp, 040h
.allocstack 040h
lea rbp, [rsp+020h]
.setframe rbp, 020h
movdqa [rbp], xmm7
.savexmm128 xmm7, 020h ;the offset is from the base of the frame
;not the scaled offset of the frame
mov [rbp+018h], rsi
.savereg rsi, 038h
mov [rsp+010h], rdi
.savereg rdi, 010h ; you can still use RSP as the base of the frame
; or any other register you choose
.endprolog
; you can modify the stack pointer outside of the prologue (similar to alloca)
; because we have a frame pointer.
; if we didn't have a frame pointer, this would be illegal
; if we didn't make this modification,
; there would be no need for a frame pointer
sub rsp, 060h
; we can unwind from the next AV because of the frame pointer
mov rax, 0
mov rax, [rax] ; AV!
; restore the registers that weren't saved with a push
; this isn't part of the official epilog, as described in section 2.5
movdqa xmm7, [rbp]
mov rsi, [rbp+018h]
mov rdi, [rbp-010h]
; Here's the official epilog
lea rsp, [rbp+020h] ; deallocate both fixed and dynamic portions of the frame
pop rbp
ret
sample ENDP
Pour plus d'informations sur l’exemple d’Épilogue, consultez Code d'épilogue dans Prologue x64 et Épilogue.
Macros MASM
Pour simplifier l’utilisation des pseudo-opérations brutes, utilisez l’ensemble de macros définies dans ksamd64.inc. Ces macros vous aident à créer des prologues et des épilogues de procédure classiques.
| Macro | Descriptif |
|---|---|
| alloc_stack(n) | Alloue une trame de pile de n octets (à l’aide de sub rsp, n) et génère les informations de déroulage appropriées (.allocstack n) |
| save_reg reg, loc | Enregistre un registre non volatile reg sur la pile au décalage RSPloc, et émet les informations de déroulement appropriées (.savereg reg, loc) |
| push_reg reg | Empile un registre non volatil reg sur la pile et génère les informations de déroulage appropriées (.pushreg reg) |
| rex_push_reg reg | Enregistre un registre nonvolatile sur la pile à l’aide d’un push de 2 octets et émet les informations de déroulement appropriées (.pushreg reg). Utilisez cette macro si le push est la première instruction de la fonction, pour vous assurer que la fonction est prête pour le correctif à chaud. |
| save_xmm128 reg, loc | Enregistre un registre non volatil XMMreg sur la pile à l’offset RSPloc et émet les informations de déroulement appropriées (.savexmm128 reg, loc) |
| set_frame reg, offset | Définit le registre de trame reg comme étant le RSP + offset (à l’aide d’un mov ou d’un lea), et émet les informations de déroulement appropriées (.set_frame reg, offset) |
| push_eflags | Envoie (push) les eflags à l’aide d’une pushfq instruction et émet les informations de déroulement appropriées (.alloc_stack 8) |
Voici un exemple de prologue de fonction avec une utilisation appropriée des macros :
sampleFrame struct
Fill dq ?; fill to 8 mod 16
SavedRdi dq ?; Saved Register RDI
SavedRsi dq ?; Saved Register RSI
sampleFrame ends
sample2 PROC FRAME
alloc_stack(sizeof sampleFrame)
save_reg rdi, sampleFrame.SavedRdi
save_reg rsi, sampleFrame.SavedRsi
.end_prolog
; function body
mov rsi, sampleFrame.SavedRsi[rsp]
mov rdi, sampleFrame.SavedRdi[rsp]
; Here's the official epilog
add rsp, (sizeof sampleFrame)
ret
sample2 ENDP
Déroulage des définitions de données en C
Voici une description en langage C des données de désenroulement :
typedef enum _UNWIND_OP_CODES {
UWOP_PUSH_NONVOL = 0, /* info == register number */
UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */
UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */
UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */
UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */
UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */
UWOP_SAVE_XMM128 = 8, /* info == XMM reg number, offset in next slot */
UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */
UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */
} UNWIND_CODE_OPS;
typedef unsigned char UBYTE;
typedef union _UNWIND_CODE {
struct {
UBYTE CodeOffset;
UBYTE UnwindOp : 4;
UBYTE OpInfo : 4;
};
USHORT FrameOffset;
} UNWIND_CODE, *PUNWIND_CODE;
#define UNW_FLAG_EHANDLER 0x01
#define UNW_FLAG_UHANDLER 0x02
#define UNW_FLAG_CHAININFO 0x04
typedef struct _UNWIND_INFO {
UBYTE Version : 3;
UBYTE Flags : 5;
UBYTE SizeOfProlog;
UBYTE CountOfCodes;
UBYTE FrameRegister : 4;
UBYTE FrameOffset : 4;
UNWIND_CODE UnwindCode[1];
/* UNWIND_CODE MoreUnwindCode[((CountOfCodes + 1) & ~1) - 1];
* union {
* OPTIONAL ULONG ExceptionHandler;
* OPTIONAL ULONG FunctionEntry;
* };
* OPTIONAL ULONG ExceptionData[]; */
} UNWIND_INFO, *PUNWIND_INFO;
typedef struct _RUNTIME_FUNCTION {
ULONG BeginAddress;
ULONG EndAddress;
ULONG UnwindData;
} RUNTIME_FUNCTION, *PRUNTIME_FUNCTION;
#define GetUnwindCodeEntry(info, index) \
((info)->UnwindCode[index])
#define GetLanguageSpecificDataPtr(info) \
((PVOID)&GetUnwindCodeEntry((info),((info)->CountOfCodes + 1) & ~1))
#define GetExceptionHandler(base, info) \
((PEXCEPTION_HANDLER)((base) + *(PULONG)GetLanguageSpecificDataPtr(info)))
#define GetChainedFunctionEntry(base, info) \
((PRUNTIME_FUNCTION)((base) + *(PULONG)GetLanguageSpecificDataPtr(info)))
#define GetExceptionDataPtr(info) \
((PVOID)((PULONG)GetLanguageSpecificData(info) + 1))