Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
Hier finden Sie eine Übersicht über die strukturierte Ausnahmebehandlung, die C++-Ausnahmebehandlung, Programmierkonventionen und Verhaltensweisen unter x64. Allgemeine Informationen zur Ausnahmebehandlung finden Sie unter Exception Handling in Microsoft C++.
Unwind-Informationen für Ausnahmebehandlung und Debuggerunterstützung
Zur Wiederherstellung nichtflüchtiger Register bei der Behandlung einer Ausnahme werden Nicht-Blatt-Funktionen mit statischen Daten versehen. Diese Daten, die gemeinhin als "Funktionsentspannungsinformationen" bezeichnet werden, beschreiben, wie die Funktion bei einer beliebigen Anweisung ordnungsgemäß entspannt wird. Diese Daten werden als pdata oder Prozedurdaten gespeichert, die sich wiederum auf xdata, die Ausnahmebehandlungsdaten, beziehen.
Die Unwind-Informationen einer Funktion bestehen aus mehreren Datenstrukturen, die im Folgenden beschrieben werden.
Informationen zu Unwind-Informationen mit Unterstützung für Intel APX (Advanced Performance Extensions) finden Sie in der Vorschau auf die Unwind-V3-Spezifikation.
Struktur RUNTIME_FUNCTION
Die tabellenbasierte Ausnahmebehandlung erfordert einen Tabelleneintrag für alle Funktionen, die Stapelspeicher zuordnen oder eine andere Funktion (z. B. Non-Leaf-Funktionen) aufrufen. Funktionstabelleneinträge weisen das folgende Format auf:
| Größe | Wert |
|---|---|
| ULONG | Startadresse der Funktion |
| ULONG | Endadresse der Funktion |
| ULONG | Unwind-Info-Adresse |
Die RUNTIME_FUNCTION Struktur muss im Speicher DWORD ausgerichtet sein. Alle Adressen sind imagerelativ, also 32-Bit-Offsets von der Startadresse des Images, das den Funktionstabelleneintrag enthält. Diese Einträge werden sortiert und in den .pdata Abschnitt eines PE32+-Bilds eingefügt. Für dynamisch generierte Funktionen [JIT-Compiler] muss die Laufzeitumgebung zur Unterstützung dieser Funktionen entweder RtlInstallFunctionTableCallback oder RtlAddFunctionTable verwenden, um diese Informationen dem Betriebssystem bereitzustellen. Wenn dies nicht der Fall ist, führt dies zu einer unzuverlässigen Ausnahmebehandlung und zum Debuggen von Prozessen.
Struktur UNWIND_INFO
Die Unwind-Informationsstruktur erfasst die Auswirkungen, die eine Funktion auf den Stapelzeiger ausübt, und wo die nichtflüchtigen Register auf dem Stapel gespeichert sind:
| Größe | Wert |
|---|---|
| UBYTE: 3 | Version |
| UBYTE: 5 | Flaggen |
| UBYTE | Prologgröße |
| UBYTE | Anzahl der Unwind-Codes |
| UBYTE: 4 | Rahmenregister |
| UBYTE: 4 | Frameregisteroffset (skaliert) |
| USHORT * n | Entwirrungscode-Array |
| Variable | Kann entweder dem folgenden Format (1) oder (2) entsprechen |
Ausnahmebehandler
| Größe | Wert |
|---|---|
| ULONG | Adresse des Ausnahmehandlers |
| Variable | Sprachspezifische Handlerdaten (optional) |
(2) Verkettete Entladeinformationen
| Größe | Wert |
|---|---|
| ULONG | Startadresse der Funktion |
| ULONG | Endadresse der Funktion |
| ULONG | Unwind-Info-Adresse |
Die UNWIND_INFO-Struktur muss im Speicher DWORD ausgerichtet sein. Die einzelnen Felder bedeuten Folgendes:
Version
Versionsnummer der Entladedaten, derzeit 1
Flaggen
Derzeit sind drei Flags definiert:
Flagge Beschreibung UNW_FLAG_EHANDLERDie Funktion verfügt über einen Ausnahmehandler, den das Betriebssystem aufruft, um den Status der Ausnahme zu untersuchen und potenziell zu behandeln. Sprachfeatures wie die C-Klausel __tryregistrieren einen solchen Handler.UNW_FLAG_UHANDLERDie Funktion verfügt über eine Beendigungsroutine, die das Betriebssystem beim Abwickeln des Stacks aufruft. Dieser Handler kann Ressourcen freigeben, die von der Funktion im Ausnahmesicheren Code zugewiesen wurden. Sprachmerkmale wie Destruktoren lokaler C++-Objekte und C- __finally-Klauseln registrieren einen solchen Beendigungshandler.UNW_FLAG_CHAININFODiese Unwind-Informationsstruktur ist nicht die primäre für die Prozedur. Stattdessen ist der verkettete Informationseintrag der Inhalt eines vorherigen RUNTIME_FUNCTION-Eintrags. Weitere Informationen finden Sie unter Verkettete Unwind-Informationsstrukturen. Wenn dieses Flag gesetzt ist, müssen die FlagsUNW_FLAG_EHANDLERundUNW_FLAG_UHANDLERzurückgesetzt werden. Außerdem müssen die Felder für das Frameregister und die feste Stapelzuordnung dieselben Werte wie die primären Entladeinformationen aufweisen.Prologgröße
Die Länge des Funktionsprologs in Bytes
Anzahl der Unwind-Codes
Die Anzahl der Slots im Array der Unwind-Codes. Einige Unwind-Codes, wie
UWOP_SAVE_NONVOL, benötigen mehr als einen Eintrag im Array.Frameregister
Wenn der Wert ungleich null ist, verwendet die Funktion einen Frame Pointer (FP), und dieses Feld ist die Nummer des nichtflüchtigen Registers, das als Framezeiger verwendet wird, wobei für das Vorgangsinfofeld von
UNWIND_CODE-Knoten dieselbe Codierung verwendet wird.Frameregisteroffset (skaliert)
Dieses Feld ist ein skalierter Offset zwischen dem
RSPRegisterwert und dem ausgewählten Frame Pointer (FP)-Registerwert. Das ausgewählte FP-Register wird aufRSP+ 16 * dieser Zahl gesetzt, d. h. Sie können Offsets im Bereich von 0 bis 240 verwenden. Dieser Offset zeigt das FP-Register auf die Mitte der lokalen Stackbelegung für dynamische Stack-Frames, sodass Sie dadurch mit kürzeren Anweisungen eine bessere Code-Dichte erzielen. (Das heißt, dass mehr Instruktionen die 8-Bit-signierte-Offset-Form verwenden können.)Unwind-Codes-Array
Ein Array von Elementen, das die Auswirkungen des Prologs auf die nichtflüchtigen Register und auf
RSPbeschreibt. Die Bedeutung der einzelnen Elemente finden Sie im Abschnitt Code für Unwind-Operationen. Um eine ordnungsgemäße Datenausrichtung aufrechtzuerhalten, enthält dieses Array immer eine gerade Anzahl von Einträgen, und der endgültige Eintrag kann nicht verwendet werden. In diesem Fall ist das Array um ein Element länger als durch das Feld für die Anzahl der Unwind-Codes angegeben.Adresse des Ausnahmehandlers
Ein bildrelativer Zeiger auf den sprachspezifischen Ausnahme- oder Beendigungshandler der Funktion, wenn die Kennzeichnung
UNW_FLAG_CHAININFOklar ist und eines der FlagsUNW_FLAG_EHANDLERUNW_FLAG_UHANDLERoder festgelegt ist.Sprachspezifische Handlerdaten
Die sprachspezifischen Ausnahmehandlerdaten der Funktion – das Format dieser Daten ist nicht angegeben und wird vollständig durch den verwendeten Ausnahmehandler bestimmt.
Verkettete Entladeinformationen
Wenn die Kennzeichnung
UNW_FLAG_CHAININFOfestgelegt ist, endet dieUNWIND_INFOStruktur mit dreiUWORDs. DieseUWORDstellen dieRUNTIME_FUNCTIONInformationen für die Funktion des verketteten Abspanns dar.
Struktur UNWIND_CODE
Verwenden Sie das Unwind-Code-Array, um die Abfolge der Vorgänge im Prolog aufzuzeichnen, die die nichtflüchtigen Register und RSP betreffen. Jedes Codeelement weist das folgende Format auf:
| Größe | Wert |
|---|---|
| UBYTE | Offset im Prolog |
| UBYTE: 4 | Code für den Entladevorgang |
| UBYTE: 4 | Vorgangsinformationen |
Das Array wird in der absteigenden Reihenfolge des Offsets im Prolog sortiert.
Offset im Prolog
Der Offset (ab dem Anfang des Prologs) des Endes der Anweisung, die diesen Vorgang ausführt, plus 1 (d. h. der Offset des Anfangs der nächsten Anweisung).
Code für den Entladevorgang
Bestimmte Operationscodes erfordern einen vorzeichenlosen Offset für einen Wert im lokalen Stack-Frame. Dieser Offset erfolgt vom Anfang her, das heißt, von der niedrigsten Adresse der festen Stack-Zuweisung. Wenn das Feld "Frame Register" in der UNWIND_INFO 0 ist, ist dieser Offset von RSP. Wenn das Feld „Frame Register“ ungleich null ist, wird dieser Offset von der Stelle aus berechnet, an der sich RSP befand, als das FP-Register eingerichtet wurde. Es entspricht dem FP-Register minus dem FP-Registeroffset (16 * der skalierte Rahmenregisterversatz im UNWIND_INFO). Wenn ein FP-Register verwendet wird, darf ein Unwind-Code, der einen Offset verwendet, nur dann eingesetzt werden, nachdem das FP-Register im Prolog etabliert wurde.
Für alle Opcodes mit Ausnahme von UWOP_SAVE_XMM128 und UWOP_SAVE_XMM128_FAR ist der Offset immer ein Vielfaches von 8, da alle relevanten Stapelwerte in 8-Byte-Grenzen gespeichert werden (der Stapel selbst ist immer auf 16 Byte ausgerichtet). Bei Operationscodes, die einen kurzen Offset (weniger als 512K) verwenden, enthält das letzte USHORT in den Knoten für diesen Code den durch 8 geteilten Offset. Für Vorgangscodes, die einen langen Offset verwenden (512K <= offset < 4GB), enthalten die letzten beiden USHORT-Knoten dieses Codes den Offset (im Little-Endian-Format).
Bei den Opcodes UWOP_SAVE_XMM128 und UWOP_SAVE_XMM128_FAR ist der Offset immer ein Vielfaches von 16, da alle 128-Bit-XMM-Operationen auf 16-Byte-ausgerichtetem Speicher erfolgen müssen. Aus diesem Grund wird ein Skalierungsfaktor von 16 für UWOP_SAVE_XMM128 verwendet, der Offsets von weniger als 1 MB zulässt.
Der Code für den Entladungsvorgang entspricht einem der folgenden Werte:
UWOP_PUSH_NONVOL(0) 1 KnotenSpeichern Sie ein nichtflüchtiges Integer-Register, wobei
RSPum 8 verringert wird. Die Vorgangsinformationen umfassen die Registernummer. Aufgrund der Einschränkungen für Epiloge müssenUWOP_PUSH_NONVOL-Entwirrungscodes zuerst im Prolog und entsprechend zuletzt im Entwirrungscode-Array erscheinen. Diese relative Reihenfolge gilt für alle weiteren Unwind-Codes außerUWOP_PUSH_MACHFRAME.UWOP_ALLOC_LARGE(1) 2 oder 3 KnotenHiermit wird dem Stapel ein großer Bereich zugewiesen. Es gibt zwei Formulare. Wenn die Vorgangsinformationen gleich 0 (null) sind, wird die Größe der Zuordnung geteilt durch 8 im nächsten Slot aufgezeichnet, sodass eine Zuordnung von bis zu 512 KB – 8 möglich ist. Wenn die Vorgangsinformationen gleich 1 sind, wird die nicht skalierte Größe der Zuordnung in den nächsten zwei Slots im Little-Endian-Format aufgezeichnet, sodass eine Zuordnung von bis zu 4 GB – 8 möglich ist.
UWOP_ALLOC_SMALL(2) 1 KnotenHiermit wird dem Stapel ein kleiner Bereich zugewiesen. Die Größe der Zuordnung ist das Vorgangsinfofeld * 8 + 8, sodass Zuordnungen von 8 bis 128 Bytes möglich sind.
Für den Entladungscode einer Stapelzuordnung sollte immer die kürzestmögliche Codierung verwendet werden:
Zuordnungsgröße Entladungscode 8 bis 128 Bytes UWOP_ALLOC_SMALL136 bis 512 KB – 8 Byte UWOP_ALLOC_LARGE, Vorgangsinformationen = 0512K bis 4G-8 Byte UWOP_ALLOC_LARGE, Vorgangsinformationen = 1UWOP_SET_FPREG(3) 1 KnotenRichten Sie das Framezeigerregister ein, indem Sie das Register auf einen bestimmten Offset relativ zum aktuellen
RSPsetzen. Der Offset entspricht dem Feld „Frame Register Offset (skaliert)“ inUNWIND_INFO, multipliziert mit 16, wodurch Offsets von 0 bis 240 möglich sind. Durch die Verwendung eines Offsets kann ein Framezeiger festgelegt werden, der auf die Mitte der festen Stapelzuordnung zeigt, wodurch die Codedichte verbessert wird, indem mehr Zugriffe auf kurze Anweisungsformen möglich sind. Das Feld für die Vorgangsinformationen ist reserviert und sollte nicht verwendet werden.UWOP_SAVE_NONVOL(4) 2 KnotenVerwenden Sie MOV statt PUSH, um ein nicht flüchtiges Integerregister im Stapel zu speichern. Dieser Code wird hauptsächlich für das Shrink Wrapping verwendet, bei dem ein nicht flüchtiges Register im Stapel an einer zuvor zugewiesenen Position gespeichert wird. Die Vorgangsinformationen umfassen die Registernummer. Der Offset des mit dem Faktor 8 skalierten Stapels wird im nächsten Unwind-Operation-Code-Slot aufgezeichnet, wie in der Anmerkung oben beschrieben.
UWOP_SAVE_NONVOL_FAR(5) 3 KnotenSpeichern Sie ein nichtflüchtiges Integer-Register mit einem langen Offset auf dem Stack, indem Sie MOV anstelle von PUSH verwenden. Dieser Code wird hauptsächlich für das Shrink Wrapping verwendet, bei dem ein nicht flüchtiges Register im Stapel an einer zuvor zugewiesenen Position gespeichert wird. Die Vorgangsinformationen umfassen die Registernummer. Der Offset des nicht skalierten Stapels wird wie zuvor beschrieben in den nächsten zwei Codeslots des Entladevorgangs aufgezeichnet.
UWOP_SAVE_XMM128(8) 2 KnotenSpeichern Sie alle 128 Bits eines nicht volatile
XMMRegisters im Stapel. Die Vorgangsinformationen umfassen die Registernummer. Der Offset des nach 16 skalierten Stapels wird im nächsten Slot aufgezeichnet.UWOP_SAVE_XMM128_FAR(9) 3 KnotenSpeichern Sie alle 128 Bit eines nichtflüchtigen
XMMRegisters im Stack mit großem Offset. Die Vorgangsinformationen umfassen die Registernummer. Der Offset des nicht skalierten Stapels wird in den nächsten beiden Slots aufgezeichnet.UWOP_PUSH_MACHFRAME(10) 1 KnotenEine Maschinenstruktur schieben. Dieser Unwindcode zeichnet die Auswirkung eines Hardwareinterrupts oder einer Ausnahme auf. Es hat zwei Formen. Ein Wert von 0 gibt an, dass die Hardware einen Frame wie dies auf dem Stapel verschoben hat:
Standort Wert RSP+32SSRSP+24Alt RSPRSP+16EFLAGSRSP+8CSRSPRIPEin Wert von 1 gibt an, dass die Hardware einen Frame wie dies auf dem Stapel verschoben hat:
Standort Wert RSP+40SSRSP+32Alt RSPRSP+24EFLAGSRSP+16CSRSP+8RIPRSPFehlercode Dieser Unwind-Code erscheint immer in einem Dummy-Prolog, der nie tatsächlich ausgeführt wird, sondern vor dem realen Einstiegspunkt einer Unterbrechungsroutine platziert ist und existiert nur, um das Schieben eines Maschinen-Frames zu simulieren.
UWOP_PUSH_MACHFRAMEzeichnet diese Simulation auf. Daraus geht hervor, dass der Computer diesen Vorgang konzeptionell ausgeführt hat:Hole die Rücksprungadresse vom Stack-Top in Temp
RIPDrücken
SSAlte Daten übertragen
RSPDrücken Sie
EFLAGSDrücken
CSTemp drücken
Pushfehlercode (wenn die Vorgangsinformationen gleich 1 sind)
Der simulierte
UWOP_PUSH_MACHFRAMEVorgang verringertRSPum 40 (wenn die Op-Info 0 ist) oder um 48 (wenn die Op-Info 1 ist).
Vorgangsinformationen
Die Bedeutung der Operationsinformationsbits hängt vom Opcode ab. Folgende Zuordnung wird verwendet, um ein allgemeines Register (Integer) zu codieren:
| bit | Registrieren |
|---|---|
| 0 | RAX |
| 1 | RCX |
| 2 | RDX |
| 3 | RBX |
| 4 | RSP |
| 5 | RBP |
| 6 | RSI |
| 7 | RDI |
| 8 bis 15 |
R8 bis R15 |
Verkettete Entladeinformationsstrukturen
Wenn das UNW_FLAG_CHAININFO Flag gesetzt ist, dann ist eine Unwind-Informationsstruktur eine sekundäre Struktur, und das gemeinsame Adressfeld für Ausnahmebehandlung/verkettete Informationen enthält die primären Unwind-Informationen. Dieser Beispielcode ruft die primären Unwindinformationen ab, wobei davon ausgegangen wird, dass unwindInfo die Struktur ist, in der das Flag UNW_FLAG_CHAININFO gesetzt ist.
PRUNTIME_FUNCTION primaryUwindInfo = (PRUNTIME_FUNCTION)&(unwindInfo->UnwindCode[( unwindInfo->CountOfCodes + 1 ) & ~1]);
Verkettete Informationen sind in zwei Situationen nützlich. Sie können einerseits für nicht zusammenhängende Codesegmente verwendet werden. Mithilfe von verketteten Informationen können Sie die Größe der erforderlichen Entspanninformationen reduzieren, da Sie das Array der Abspanncodes nicht aus den primären Entspanninformationen duplizieren müssen.
Sie können verkettete Informationen andererseits verwenden, um flüchtige Registerspeicherungen zu gruppieren. Der Compiler kann das Speichern einiger veränderliche Register verzögern, bis er sich außerhalb des Prologs des Funktionseintrags befindet. Sie können sie aufzeichnen, indem Sie für den Teil der Funktion vor dem gruppierten Code primäre Unwind-Informationen verwenden und dann verkettete Informationen mit einer Prologgröße ungleich null einrichten, wobei die Unwind-Codes in den verketteten Informationen das Sichern der nichtflüchtigen Register widerspiegeln. In diesem Fall sind die Unwind-Codes allesamt Instanzen von UWOP_SAVE_NONVOL. Eine Gruppierung, die nichtflüchtige Register mithilfe von PUSH sichert oder das Register RSP mithilfe einer zusätzlichen festen Stackzuweisung ändert, wird nicht unterstützt.
Ein UNWIND_INFO Element, das UNW_FLAG_CHAININFO gesetzt hat, kann einen RUNTIME_FUNCTION-Eintrag enthalten, dessen UNWIND_INFO Element ebenfalls UNW_FLAG_CHAININFO gesetzt hat, was manchmal als mehrfache Schrumpfverpackung bezeichnet wird. Schließlich gelangen die verketteten Unwind-Informationszeiger zu einem UNWIND_INFO Element, bei dem UNW_FLAG_CHAININFO nicht gesetzt ist. Dieses Element ist das primäre UNWIND_INFO Element, das auf den tatsächlichen Einstiegspunkt der Prozedur verweist.
Entladeprozedur
Das Unwind-Code-Array wird in absteigender Reihenfolge sortiert. Wenn eine Ausnahme auftritt, speichert das Betriebssystem den vollständigen Kontext in einem Kontextdatensatz. Anschließend wird die Dispatchlogik für Ausnahmen aufgerufen, die wiederholt die folgenden Schritte ausführt, um einen Ausnahmehandler zu finden:
Verwenden Sie den aktuell im Kontextdatensatz gespeicherten
RIP, um nach einemRUNTIME_FUNCTION-Tabelleneintrag zu suchen, der die aktuelle Funktion (oder den Funktionsteil bei verkettetenUNWIND_INFO-Einträgen) beschreibt.Wenn die Suche keinen Funktionstabelleneintrag findet, wird davon ausgegangen, dass der Code Teil einer Blattfunktion ist und
RSPden Rückgabezeiger direkt adressiert. Der Rücksprungzeiger an [RSP] wird im aktualisierten Kontext gespeichert, der simulierteRSPwird um 8 inkrementiert, und Schritt 1 wird wiederholt.Wenn die Suche einen Funktionstabelleneintrag findet,
RIPkann in drei Bereichen liegen: a) in einem Epilog, b) im Prolog oder c) im Code, der möglicherweise von einem Ausnahmehandler abgedeckt wird.Fall a) Wenn sich
RIPinnerhalb eines Epilogs befindet, verlässt die Kontrolle die Funktion. Dieser Ausnahme kann kein Ausnahmehandler für diese Funktion zugeordnet sein. Die Effekte des Epilogs müssen weiterhin den Kontext der aufrufenden Funktion ermitteln. Um festzustellen, ob sichRIPinnerhalb eines Epilogs befindet, wird der Codestrom abRIPuntersucht. Wenn dieser Codestrom dem abschließenden Teil eines gültigen Epilogs entspricht, dann befindet er sich in einem Epilog. Der verbleibende Teil des Epilogs wird simuliert, wobei der Kontextdatensatz aktualisiert wird, während jede Anweisung verarbeitet wird. Nach diesem Vorgang wird Schritt 1 wiederholt.- Fall b) Wenn das
RIPSteuerelement innerhalb des Prologs liegt, hat das Steuerelement die Funktion nicht eingegeben. Dieser Ausnahme kann kein Ausnahmehandler für diese Funktion zugeordnet sein. Die Auswirkungen des Prologs müssen rückgängig gemacht werden, um den Kontext der Aufruferfunktion zu berechnen. DasRIPbefindet sich im Prolog, wenn der Abstand vom Funktionsanfang bis zumRIPkleiner oder gleich der in den Unwind-Informationen codierten Prologgröße ist. Der Unwinder durchsucht das Array der Unwind-Codes vorwärts nach dem ersten Eintrag mit einem Offset, der kleiner oder gleich dem Offset vonRIPrelativ zum Funktionsanfang ist, und macht dann die Wirkung aller verbleibenden Einträge im Array der Unwind-Codes rückgängig. Anschließend wird Schritt 1 wiederholt.
- Fall b) Wenn das
Fall c) Wenn sich
RIPnicht innerhalb eines Prologs oder Epilogs befindet und die Funktion einen Exception-Handler hat (UNW_FLAG_EHANDLERist gesetzt), wird der sprachspezifische Handler aufgerufen. Der Handler scannt die Daten und ruft entsprechend die Filterfunktionen auf. Der sprachspezifische Handler kann zurückgeben, dass die Ausnahme behandelt wurde oder dass die Suche fortgesetzt werden soll. Außerdem kann eine Entladung direkt initiiert werden.
Wenn der sprachspezifische Handler einen behandelten Status zurückgibt, wird die Ausführung mit dem ursprünglichen Kontextdatensatz fortgesetzt.
Wenn kein sprachspezifischer Handler vorhanden ist oder der Handler den Status „Suche fortsetzen“ zurückgibt, muss der Kontextdatensatz auf den Zustand des Aufrufers zurückgesetzt werden. Der Unwinder macht die Wirkung jedes Elements im Unwind-Code-Array rückgängig. Anschließend wird Schritt 1 wiederholt.
Wenn verkettete Entladeinformationen vorhanden sind, werden diese grundlegenden Schritte trotzdem befolgt. Der einzige Unterschied besteht darin, dass beim Durchlaufen des Unwind-Code-Arrays, um die Auswirkungen eines Prologs rückgängig zu machen, nach Erreichen des Endes des Arrays auf die übergeordneten Unwind-Informationen verwiesen wird und dort das gesamte Unwind-Code-Array durchlaufen wird. Diese Verknüpfung wird fortgesetzt, bis sie zu einer Entlastungsinfo ohne Kennzeichnung UNW_CHAINED_INFO gelangt ist, und dann endet sie mit dem Durchlaufen des Entlastungscodearrays.
Der kleinste Satz von Unwind-Daten beträgt 8 Byte. Dieser Satz stellt eine Funktion dar, die nur 128 Bytes Stapel oder weniger zugeordnet und möglicherweise ein nicht volatiles Register gespeichert hat. Der Wert entspricht auch der Größe einer verketteten Unwind-Informationsstruktur für einen Null-Längen-Prolog ohne Unwind-Codes.
Sprachspezifische Handler
Die UNWIND_INFO Struktur stellt die relative Adresse des sprachspezifischen Handlers bereit, wenn entweder UNW_FLAG_EHANDLER oder UNW_FLAG_UHANDLER Flags festgelegt werden. Wie im vorherigen Abschnitt beschrieben, ruft die Suche nach einem Ausnahmehandler oder dem Abwickelvorgang den sprachspezifischen Handler auf. Der Handler verwendet diesen Prototyp:
typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE) (
IN PEXCEPTION_RECORD ExceptionRecord,
IN ULONG64 EstablisherFrame,
IN OUT PCONTEXT ContextRecord,
IN OUT PDISPATCHER_CONTEXT DispatcherContext
);
ExceptionRecord stellt einen Zeiger auf einen Ausnahmedatensatz bereit, der der Standarddefinition für Win64 folgt.
EstablisherFrame ist die Adresse der Basis der festen Stapelzuordnung für diese Funktion.
ContextRecord zeigt auf den Ausnahmekontext zu dem Zeitpunkt, als die Ausnahme ausgelöst wurde (im Fall eines Ausnahmehandlers), oder auf den aktuellen Entladekontext (im Fall eines Beendigungshandlers).
DispatcherContext verweist auf den Dispatcherkontext für diese Funktion. Hierbei gilt folgende Definition:
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 ist der Wert dieser RIP Funktion. Dieser Wert ist entweder eine Ausnahmeadresse oder die Adresse, an der das Steuerelement die erstellende Funktion verlassen hat. Dies RIP wird verwendet, um zu ermitteln, ob sich das Steuerelement innerhalb eines geschützten Konstrukts innerhalb dieser Funktion befindet, z. B. einen __try Block für__try/__except oder .__try/__finally
ImageBase ist die Bildbasis (Ladeadresse) des Moduls, das diese Funktion enthält. Die im Funktionseintrag und in den Unwindinformationen verwendeten 32-Bit-Offsets sollten zur ImageBase hinzugefügt werden, um die endgültige Adresse zu erhalten.
FunctionEntry liefert einen Zeiger auf den RUNTIME_FUNCTIONFunktionseintrag, der die bildbasisrelativen Adressen der Funktion und der Unwindinformationen für diese Funktion enthält.
EstablisherFrame ist die Adresse der Basis der festen Stapelzuordnung für diese Funktion.
TargetIp liefert eine optionale Instruktionsadresse, die die Fortsetzungsadresse des Unwind-Vorgangs angibt. Diese Adresse wird ignoriert, wenn EstablisherFrame nicht angegeben wird.
ContextRecord zeigt auf den Ausnahmekontext, der vom Ausnahmeverteiler-/Rückwicklungscode des Systems verwendet werden soll.
LanguageHandler zeigt auf die sprachspezifische Handlerroutine, die aufgerufen wird.
HandlerData zeigt auf die sprachspezifischen Handlerdaten für diese Funktion.
Entladehilfsprogramme für MASM
Verwenden Sie zum Schreiben ordnungsgemäßer Assemblyroutinen eine Reihe von Pseudovorgängen zusammen mit den tatsächlichen Assemblyanweisungen. Diese Pseudovorgänge erstellen die entsprechenden .pdata und .xdata. Verwenden Sie außerdem eine Reihe von Makros, die die Verwendung dieser Pseudovorgänge für die am häufigsten verwendeten Verwendungen vereinfachen.
Roh-Pseudovorgänge
| Pseudovorgänge | Beschreibung |
|---|---|
| PROC FRAME [:ehandler] | Bewirkt, dass MASM einen Eintrag in der Funktionstabelle in .pdata und Unwindinformationen in .xdata für das strukturierte Ausnahmebehandlungs-Unwindverhalten einer Funktion generiert. Wenn ehandler vorhanden ist, wird diese Prozedur im .xdata als der sprachspezifische Handler eingetragen.Wenn Sie das FRAME-Attribut verwenden, lassen Sie darauf eine .ENDPROLOG-Direktive folgen. Wenn es sich bei der Funktion um eine Blattfunktion handelt (wie in Funktionstypen definiert), ist das FRAME-Attribut nicht erforderlich, ebenso wie der Rest dieser Pseudovorgänge. |
| .PUSHREG Register | Generiert einen UWOP_PUSH_NONVOL Unwindcode-Eintrag für die angegebene Registernummer unter Verwendung des aktuellen Offsets im Prolog.Verwenden Sie diesen nur mit nicht flüchtigen Integerregistern. Verwenden Sie stattdessen für Pushes von flüchtigen Registern .ALLOCSTACK 8. |
| .SETFRAME Register, Offset | Füllt das Frameregisterfeld und den Offset in den Entladeinformationen mit dem angegebenen Register und Offset aus. Der Offset muss ein Vielfaches von 16 und kleiner oder gleich 240 sein. Diese Direktive generiert auch einen UWOP_SET_FPREG Unwindcode-Eintrag für das angegebene Register unter Verwendung des aktuellen Prolog-Offsets. |
| .ALLOCSTACK Größe | Generiert ein UWOP_ALLOC_SMALL oder ein UWOP_ALLOC_LARGE mit der angegebenen Größe für den aktuellen Offset im Prolog.Der Größe-Operand muss ein Vielfaches von 8 sein. |
| .SAVEREG Register, Offset | Generiert entweder einen UWOP_SAVE_NONVOL- oder einen UWOP_SAVE_NONVOL_FAR-Unwindcode-Eintrag für das angegebene Register und den Offset unter Verwendung des aktuellen Prolog-Offsets. MASM wählt die effizienteste Codierung aus.Der Offset muss positiv und ein Vielfaches von 8 sein. offset ist relativ zur Basis des Frames der Prozedur, die sich in der Regel in RSP oder, bei Verwendung eines Frame-Pointers, im unskalierten Frame-Pointer befindet. |
| .SAVEXMM128 Register, Offset | Generiert unter Verwendung des aktuellen Prolog-Offsets entweder einen UWOP_SAVE_XMM128-Eintrag oder einen UWOP_SAVE_XMM128_FAR-Unwindcodeeintrag für das angegebene Register XMM und den Offset. MASM wählt die effizienteste Codierung aus.Der Offset muss positiv und ein Vielfaches von 16 sein. offset bezieht sich auf die Basis des Stackframes der Prozedur, die sich im Allgemeinen in RSP befindet, oder, bei Verwendung eines Frame-Pointers, auf den unskalierten Frame-Pointer. |
| .PUSHFRAME [Code] | Generiert einen UWOP_PUSH_MACHFRAME Unwind-Code-Eintrag. Wenn Sie den optionalen code angeben, erhält der Unwind-Code-Eintrag den Modifier 1. Andernfalls wird der Modifizierer 0 angewendet. |
| .ENDPROLOG | Kennzeichnet das Ende der Prolog-Erklärungen. muss in den ersten 255 Byte der Funktion auftreten. |
Im Folgenden finden Sie ein Beispiel für einen Funktionsprolog mit korrekter Verwendung der meisten 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
Weitere Informationen zum Epilogbeispiel finden Sie im Artikel Prolog und Epilog bei x64-Systemen unter Epilogcode.
MASM-Makros
Um die Verwendung von Unformatierten Pseudovorgängen zu vereinfachen, verwenden Sie den Satz von Makros, die in ksamd64.incdefiniert sind. Mit diesen Makros können Sie typische Prozedurprologe und Epiloge erstellen.
| Makro | Beschreibung |
|---|---|
| alloc_stack(n) | Weist einen Stackframe von n Bytes (unter Verwendung von sub rsp, n) zu, und erzeugt die entsprechenden Unwind-Informationen (.allocstack n) |
| save_reg reg, loc | Speichert ein nichtflüchtiges Register reg auf dem Stapel bei RSP Offset loc und erzeugt die entsprechenden Unwind-Informationen (.savereg reg, loc) |
| push_reg register | Legt ein nichtflüchtiges Register reg auf dem Stack ab und erzeugt die entsprechenden Unwind-Informationen (.pushreg reg). |
| rex_push_reg reg | Speichert ein nicht unvolatiles Register auf dem Stapel mithilfe eines 2-Byte-Pushs und gibt die entsprechenden Entspanninformationen (.pushreg reg) aus. Verwenden Sie dieses Makro, wenn der Push die erste Anweisung in der Funktion ist, damit sichergestellt wird, dass die Funktion hotpatchfähig ist. |
| save_xmm128 reg, loc | Speichert ein nichtflüchtiges XMM Register reg auf dem Stack bei RSP Offset loc und erzeugt die entsprechenden Unwindinformationen (.savexmm128 reg, loc) |
| set_frame reg, offset | Legt das Frame-Register reg auf den RSP + Offset fest (unter Verwendung eines mov oder eines lea) und gibt die entsprechenden Unwind-Informationen aus (.set_frame reg, offset) |
| push_eflags | Verschiebt die Eflags mithilfe einer pushfq Anweisung und gibt die entsprechenden Entschlackungsinformationen (.alloc_stack 8) aus. |
Hier ist eine Beispielfunktion prolog mit der richtigen Verwendung der Makros:
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
Definitionen von Unwind-Daten in C
Hier ist eine C-Beschreibung der Unwind-Daten:
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))