Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Omówienie obsługi wyjątków strukturalnych i konwencji kodowania obsługi wyjątków w języku C++ oraz zachowania w architekturze x64. Aby uzyskać ogólne informacje na temat obsługi wyjątków, zobacz Obsługa wyjątków w języku Microsoft C++.
Odwij dane na potrzeby obsługi wyjątków i obsługi debugera
Aby odzyskać rejestry nieulotne w przypadku obsługi wyjątku, funkcje nieliściaste są oznaczone danymi statycznymi. Te dane, często określane jako „informacje o rozwijaniu stosu funkcji”, opisują, jak prawidłowo rozwijać stos funkcji na poziomie dowolnej instrukcji. Te dane są przechowywane jako dane pdata lub dane procedury, które z kolei odnoszą się do xdata, danych obsługi wyjątków.
Funkcja Unwind Information składa się z kilku struktur danych opisanych w dalszej części.
Aby uzyskać informacje o funkcji Unwind obsługujące rozszerzenia Intel APX (Advanced Performance Extensions), zobacz Specyfikację unwind V3 Preview.
struktura RUNTIME_FUNCTION
Obsługa wyjątków opartych na tabelach wymaga wpisu tabeli dla wszystkich funkcji, które przydzielają przestrzeń stosu lub wywołują inną funkcję (na przykład funkcje niebędące funkcjami liścia). Wpisy tabeli funkcji mają format:
| Rozmiar | Wartość |
|---|---|
| ULONG | Adres początkowy funkcji |
| ULONG | Adres końcowy funkcji |
| ULONG | Odwij adres informacyjny |
Struktura RUNTIME_FUNCTION musi być DWORD wyrównana w pamięci. Wszystkie adresy są względne względem obrazu, innymi słowy, są to 32-bitowe przesunięcia od adresu początkowego obrazu zawierającego wpis tabeli funkcji. Te wpisy są sortowane i umieszczane w sekcji .pdata obrazu PE32+. W przypadku funkcji generowanych dynamicznie [kompilatorów JIT] środowisko uruchomieniowe obsługujące te funkcje musi albo używać RtlInstallFunctionTableCallback, albo RtlAddFunctionTable, aby udostępnić te informacje systemowi operacyjnemu. Niepowodzenie w tym celu powoduje zawodną obsługę wyjątków i debugowanie procesów.
struktura UNWIND_INFO
Struktura informacji o danych odwijanych rejestruje wpływ funkcji na wskaźnik stosu i miejsce, w którym rejestry nonvolatile są zapisywane na stosie:
| Rozmiar | Wartość |
|---|---|
| UBYTE: 3 | Wersja |
| UBYTE: 5 | Flagi |
| UBYTE | Rozmiar prologu |
| UBYTE | Liczba kodów zwijania |
| UBYTE: 4 | Rejestr ramek |
| UBYTE: 4 | Przesunięcie rejestru ramki (skalowane) |
| USHORT * n | Odwij tablicę kodów |
| zmienna | Może mieć formę (1) lub (2) poniżej |
(1) Program obsługi wyjątków
| Rozmiar | Wartość |
|---|---|
| ULONG | Adres programu obsługi wyjątków |
| zmienna | Dane programu obsługi specyficzne dla języka (opcjonalnie) |
(2) Informacje o łańcuchu unwind
| Rozmiar | Wartość |
|---|---|
| ULONG | Adres początkowy funkcji |
| ULONG | Adres końcowy funkcji |
| ULONG | Odwij adres informacyjny |
Struktura UNWIND_INFO musi być DWORD wyrównana w pamięci. Oto, co oznacza każde pole:
Wersja
Numer wersji danych rozprzęgania, obecnie 1.
Flagi
Obecnie zdefiniowano trzy flagi:
Flaga opis UNW_FLAG_EHANDLERFunkcja ma program obsługi wyjątków, który system operacyjny wywołuje w celu zbadania stanu wyjątku i potencjalnie go obsłuży. Funkcje językowe, takie jak klauzula C __try, rejestrują taką procedurę obsługi.UNW_FLAG_UHANDLERFunkcja ma procedurę obsługi zakończenia, którą system operacyjny wywołuje podczas odwijania stosu. Ta procedura obsługi może zwolnić zasoby przydzielone przez funkcję w bezpiecznym kodzie wyjątku. Funkcje języka, takie jak lokalne destruktory obiektów języka C++ i klauzule języka C __finally, rejestrują taką procedurę obsługi zakończenia.UNW_FLAG_CHAININFOTa struktura informacji nie jest podstawową strukturą procedury. Zamiast tego wpis informacji o łańcuchu jest zawartością poprzedniego RUNTIME_FUNCTIONwpisu. Aby uzyskać informacje, zapoznaj się z Łańcuchowymi strukturami danych do rozwijania. Jeśli ta flaga jest ustawiona, należy wyczyścić flagiUNW_FLAG_EHANDLERiUNW_FLAG_UHANDLER. Ponadto pola rejestru ramek i alokacji stosu stałego muszą mieć takie same wartości, jak w podstawowych informacjach dotyczących odwęzłowania.Rozmiar prologu
Długość prologu funkcji w bajtach.
Liczba kodów odwijania
Liczba slotów w tablicy kodów odwijania. Niektóre kody odwijania, takie jak
UWOP_SAVE_NONVOL, wymagają więcej niż jednego elementu w tablicy.Rejestr ramek
Jeśli wartość jest niezerowa, funkcja używa wskaźnika ramki (FP), a to pole zawiera numer rejestru niewolatilnego używanego jako wskaźnik ramki, przy użyciu tego samego kodowania co dla pola informacji o operacji w węzłach
UNWIND_CODE.Przesunięcie rejestru ramowego (skalowane)
To pole jest przeskalowanym przesunięciem między wartością
RSPrejestru a wybraną wartością rejestru wskaźnika ramowego (FP). Wybrany rejestr FP jest ustawiony naRSP+ 16 * tej liczby, co oznacza, że można użyć przesunięć z zakresu od 0 do 240. To przesunięcie ustawia rejestr FP na środku obszaru lokalnej alokacji na stosie dla dynamicznych ramek stosu, dzięki czemu można uzyskać większą gęstość kodu dzięki krótszym instrukcjom. (Oznacza to, że więcej instrukcji może używać 8-bitowego formularza przesunięcia z podpisem).Odwij tablicę kodów
Tablica elementów, które wyjaśniają wpływ prologu na rejestry nonvolatile i
RSP. Zobacz sekcję dotyczącą kodu operacji odwijania, aby poznać znaczenie poszczególnych elementów. Aby zachować odpowiednie wyrównanie danych, ta tablica zawsze zawiera parzystą liczbę wpisów, a ostateczny wpis może być nieużywany. W takim przypadku tablica jest jedną dłuższą niż wskazywana przez liczbę pól kodów odwijanych.Adres programu obsługi wyjątków
Wskaźnik względny obrazu do wyjątku specyficznego dla języka funkcji lub procedury obsługi zakończenia, jeśli flaga
UNW_FLAG_CHAININFOjest jasna i jedna z flagUNW_FLAG_EHANDLERlubUNW_FLAG_UHANDLERjest ustawiona.Dane programu obsługi specyficzne dla języka
Dane obsługi wyjątków specyficzne dla języka danej funkcji. Format tych danych jest nieokreślony i całkowicie określany przez określoną procedurę obsługi wyjątków w użyciu.
Informacje o rozpiętym łańcuchu
Jeśli flaga
UNW_FLAG_CHAININFOjest ustawiona, strukturaUNWIND_INFOkończy się trzema elementamiUWORD. TeUWORDreprezentują informacjeRUNTIME_FUNCTIONdla funkcji łańcuchowego odwijania.
struktura UNWIND_CODE
Użyj tablicy kodu unwind, aby zarejestrować sekwencję operacji w prologu, które mają wpływ na niewolne rejestry i RSP. Każdy element kodu ma następujący format:
| Rozmiar | Wartość |
|---|---|
| UBYTE | Przesunięcie w prologu |
| UBYTE: 4 | Odwij kod operacji |
| UBYTE: 4 | Informacje o operacji |
Tablica jest sortowana według malejącej kolejności przesunięcia w prologu.
Przesunięcie w prologu
Przesunięcie (od początku prologu) końca instrukcji, która wykonuje tę operację, plus 1 (czyli przesunięcie początku następnej instrukcji).
Odwij kod operacji
Niektóre kody operacji wymagają niepodpisanego przesunięcia do wartości w ramce stosu lokalnego. To przesunięcie rozpoczyna się od początku, czyli najniższego adresu przydziału stałego stosu. Jeśli pole Frame Register w UNWIND_INFO ma wartość zero, to przesunięcie jest liczone względem RSP. Jeśli pole rejestru ramki jest niezerowe, to to przesunięcie jest liczone od miejsca, w którym znajdował się RSP w chwili ustanowienia rejestru FP. Jest równy rejestrowi FP minus przesunięcie rejestru FP (16 * przeskalowane przesunięcie rejestru ramki w elemencie UNWIND_INFO). Jeśli jest używany rejestr FP, każdy kod rozwijający z przesunięciem musi być używany tylko po ustanowieniu rejestru FP w prologu.
Dla wszystkich kodów opcode z wyjątkiem UWOP_SAVE_XMM128 i UWOP_SAVE_XMM128_FAR, przesunięcie jest zawsze wielokrotnością 8, ponieważ wszystkie ważne wartości stosu są przechowywane na 8-bajtowych granicach (sam stos jest zawsze wyrównany do 16 bajtów). W przypadku kodów operacyjnych, które przyjmują krótkie przesunięcie (mniejsze niż 512K), końcowy element USHORT w węzłach dla tego kodu zawiera przesunięcie podzielone przez 8. W przypadku kodów operacyjnych, które przyjmują duże przesunięcie (512K <= przesunięcie < 4 GB), ostatnie dwa węzły USHORT dla tego kodu przechowują przesunięcie (w formacie little-endian).
W przypadku kodów operacji UWOP_SAVE_XMM128 i UWOP_SAVE_XMM128_FAR przesunięcie jest zawsze wielokrotnością 16, ponieważ wszystkie 128-bitowe operacje XMM muszą być wykonywane w pamięci wyrównanej do granicy 16 bajtów. W związku z tym do UWOP_SAVE_XMM128 używany jest współczynnik skali 16, co pozwala na przesunięcia mniejsze niż 1M.
Kod operacji odwijenia jest jedną z następujących wartości:
UWOP_PUSH_NONVOL(0) 1 węzełWypchnij rejestr liczby całkowitej bezvolatile, co spowoduje dekrementację
RSPprzez 8. Informacja o operacji to numer rejestru. Ze względu na ograniczenia dotyczące epilogówUWOP_PUSH_NONVOLkody odwijania muszą pojawiać się najpierw w prologu i odpowiednio, ostatnie w tablicy kodów odwijania. To względne porządkowanie ma zastosowanie do wszystkich innych kodów odwijania z wyjątkiemUWOP_PUSH_MACHFRAME.UWOP_ALLOC_LARGE(1) 2 lub 3 węzłyPrzydziel duży obszar na stosie. Istnieją dwie formy. Jeśli informacje o operacji są równe 0, to rozmiar alokacji podzielony przez 8 jest rejestrowany w następnym slocie, umożliwiając alokację do 512K - 8. Jeśli informacja o operacji jest równa 1, wówczas nieskalowany rozmiar alokacji jest rejestrowany w następnych dwóch miejscach w formacie little-endian, co pozwala na alokacje do 4 GB minus 8.
UWOP_ALLOC_SMALL(2) 1 węzełPrzydziel niewielki obszar na stosie. Rozmiar alokacji to pole informacji o operacji * 8 + 8, co umożliwia alokacje z 8 do 128 bajtów.
Kod odwijenia alokacji stosu powinien zawsze używać najkrótszego możliwego kodowania:
Rozmiar alokacji Odwij kod Od 8 do 128 bajtów UWOP_ALLOC_SMALLOd 136 do 512K-8 bajtów UWOP_ALLOC_LARGE, informacje o operacji = 0Od 512K do 4G - 8 bajtów UWOP_ALLOC_LARGE, informacje o operacji = 1UWOP_SET_FPREG(3) 1 węzełUstaw rejestr wskaźnika ramki, ustawiając go na wartość będącą przesunięciem względem bieżącego
RSP. Przesunięcie jest równe polu przesunięcia rejestru ramowego (skalowanemu) wUNWIND_INFO, pomnożonemu przez 16, co daje zakres przesunięcia od 0 do 240. Użycie przesunięcia pozwala na ustanowienie wskaźnika ramek, wskazującego na środek alokacji stosu stałego, co zwiększa gęstość kodu, umożliwiając większej liczbie dostępów korzystanie z krótkich form instrukcji. Pole informacji o operacji jest zarezerwowane i nie powinno być używane.UWOP_SAVE_NONVOL(4) 2 węzłyZapisz nieulotny rejestr liczb całkowitych na stosie, używając instrukcji MOV zamiast PUSH. Ten kod jest używany głównie do owijania kurczowego, gdzie rejestr nieulotny jest zapisywany na stosie w pozycji, która została wcześniej przydzielona. Informacja o operacji to numer rejestru. Przesunięcie stosu skalowanego po 8 jest rejestrowane w następnym miejscu kodu operacji odwijanej, zgodnie z opisem w powyższej notatce.
UWOP_SAVE_NONVOL_FAR(5) 3 węzłyZapisz nieulotny rejestr liczby całkowitej na stosie z dużym przesunięciem przy użyciu MOV zamiast PUSH. Ten kod jest używany głównie do owijania kurczowego, gdzie rejestr nieulotny jest zapisywany na stosie w pozycji, która została wcześniej przydzielona. Informacja o operacji to numer rejestru. Przesunięcie stosu nieskalowanego jest rejestrowane w dwóch następnych slotach kodu operacji odwijania zgodnie z opisem w powyższej notatce.
UWOP_SAVE_XMM128(8) 2 węzłyZapisz wszystkie 128 bitów nielotnego rejestru
XMMna stosie. Informacja o operacji to numer rejestru. Przesunięcie stosu skalowane przez 16 jest rejestrowane w następnym gnieździe.UWOP_SAVE_XMM128_FAR(9) 3 węzłyZapisz wszystkie 128 bitów nieulotnego rejestru
XMMna stosie z dużym przesunięciem. Informacja o operacji to numer rejestru. Przesunięcie stosu nieskalowanego jest rejestrowane w dwóch następnych miejscach.UWOP_PUSH_MACHFRAME(10) 1 węzełPrzesuń ramę maszyny. Ten kod odwijania rejestruje skutki przerwania sprzętowego lub wyjątku. Ma dwie formy. Wartość 0 wskazuje, że sprzęt umieścił na stosie ramkę taką jak ta:
Lokalizacja Wartość RSP+32SSRSP+24Stary RSPRSP+16EFLAGSRSP+8CSRSPRIPWartość 1 wskazuje, że sprzęt umieścił na stosie ramkę taką jak ta:
Lokalizacja Wartość RSP+40SSRSP+32Stary RSPRSP+24EFLAGSRSP+16CSRSP+8RIPRSPKod błędu Ten kod odwijania zawsze pojawia się w fikcyjnym prologu, który nigdy nie jest wykonywany, ale zamiast tego pojawia się przed rzeczywistym punktem wejścia procedury przerwania i istnieje tylko w celu zapewnienia miejsca symulowania wypychania ramki maszyny.
UWOP_PUSH_MACHFRAMErejestruje tę symulację, która wskazuje, że maszyna wykonała tę operację koncepcyjnie:Pobierz adres powrotny
RIPze szczytu stosu do TempNaciśnij
SSWypchnij stare
RSPNaciśnij
EFLAGSNaciśnij
CSPush Temp
Kod błędu push (jeśli informacje operacyjne są równe 1)
Symulowana operacja
UWOP_PUSH_MACHFRAMEzmniejsza wartośćRSPo 40 (jeśli informacja o operacji ma wartość 0) lub o 48 (jeśli informacja o operacji ma wartość 1).
Informacje o operacji
Znaczenie bitów informacji o operacji zależy od kodu operacji. Aby zakodować rejestr ogólnego przeznaczenia, stosuje się to mapowanie:
| Bit | Zarejestruj |
|---|---|
| 0 | RAX |
| 1 | RCX |
| 2 | RDX |
| 3 | RBX |
| 4 | RSP |
| 5 | RBP |
| 6 | RSI |
| 7 | RDI |
| Od 8 do 15 |
R8 do R15 |
Struktury informacji z odwijania łańcuchowego
Jeśli flaga UNW_FLAG_CHAININFO jest ustawiona, struktura informacji odwijania jest strukturą pomocniczą, a współdzielone pole adresowe programu obsługi wyjątków/informacji łańcuchowej zawiera podstawowe informacje odwijania. Ten przykładowy kod pobiera podstawowe informacje o odwijeniu przy założeniuunwindInfo, że UNW_FLAG_CHAININFO jest to struktura, która zawiera zestaw flag.
PRUNTIME_FUNCTION primaryUwindInfo = (PRUNTIME_FUNCTION)&(unwindInfo->UnwindCode[( unwindInfo->CountOfCodes + 1 ) & ~1]);
Informacje łańcuchowe są przydatne w dwóch sytuacjach. Najpierw można go używać w przypadku nieciągliwych segmentów kodu. Korzystając z połączonych informacji, można zmniejszyć rozmiar wymaganych informacji o odwijaniu, ponieważ nie trzeba duplikować tablicy kodów odwijania z podstawowych informacji o odwijaniu.
Można również użyć informacji łańcuchowych do grupowania nietrwałych zapisów rejestru. Kompilator może opóźnić zapisanie niektórych rejestrów ulotnych do momentu wyjścia poza prolog funkcji. Można je zarejestrować, mając podstawowe informacje rozwijania stosu dla części funkcji poprzedzającej zgrupowany kod, a następnie konfigurując informacje łańcuchowe z niezerowym rozmiarem prologu, gdzie kody rozwijania stosu w informacjach łańcuchowych odzwierciedlają zapisy rejestrów nieulotnych. W takim przypadku wszystkie kody rozwijania stosu są wystąpieniami elementu UWOP_SAVE_NONVOL. Grupowanie, które zapisuje rejestry nieulotne przy użyciu PUSH lub modyfikuje rejestr RSP przy użyciu dodatkowej stałej alokacji stosu, nie jest obsługiwane.
Element UNWIND_INFO, dla którego ustawiono UNW_FLAG_CHAININFO, może zawierać wpis RUNTIME_FUNCTION, którego element UNWIND_INFO również ma ustawione UNW_FLAG_CHAININFO, co bywa nazywane multiple shrink-wrapping. Ostatecznie łańcuchowe wskaźniki do informacji unwind prowadzą do elementu UNWIND_INFO, w którym pole UNW_FLAG_CHAININFO jest wyczyszczone. Ten element jest elementem podstawowym UNWIND_INFO , który wskazuje rzeczywisty punkt wejścia procedury.
Procedura odwijania
Tablica kodów odwijania jest uporządkowana w malejącym porządku. W przypadku wystąpienia wyjątku system operacyjny przechowuje pełny kontekst w rekordzie kontekstu. Następnie wywoływana jest logika wysyłania wyjątków, która wielokrotnie wykonuje następujące kroki, aby znaleźć procedurę obsługi wyjątków:
Użyj bieżącej wartości
RIPprzechowywanej w rekordzie kontekstu, aby odszukać wpis w tabeli opisujący bieżącą funkcjęRUNTIME_FUNCTION(lub jej część, w przypadku łańcuchowych wpisówUNWIND_INFO).Jeśli wyszukiwanie nie znajdzie wpisu tabeli funkcji, przyjmuje się, że kod jest częścią funkcji liścia i
RSPbezpośrednio adresuje wskaźnik zwracany. Wskaźnik powrotu pod adresem [RSP] jest zapisywany w zaktualizowanym kontekście, symulowany wskaźnikRSPjest inkrementowany o 8, a następnie powtarza się krok 1.Jeśli wyszukiwanie znajdzie wpis tabeli funkcji,
RIPmoże znajdować się w trzech regionach: a) w epilogu, b) w prologu lub c) w kodzie, który może być objęty procedurą obsługi wyjątków.Przypadek a) Jeśli
RIPelement znajduje się w epilogu, kontrolka opuszcza funkcję. Nie może istnieć program obsługi wyjątków skojarzony z tym wyjątkiem dla tej funkcji. Efekty epilogu muszą nadal wyznaczać kontekst funkcji wywołującej. Aby ustalić, czy elementRIPznajduje się w epilogu, analizuje się strumień kodu począwszy odRIP. Jeśli ten strumień kodu pasuje do końcowego fragmentu prawidłowego epilogu, oznacza to, że znajduje się w epilogu. Pozostała część epilogu jest symulowana z rekordem kontekstu zaktualizowanym podczas przetwarzania każdej instrukcji. Po wykonaniu tego przetwarzania krok 1 zostanie powtórzony.- Przypadek b) Jeśli
RIPznajduje się w prologu, sterowanie nie weszło jeszcze do funkcji. Nie może istnieć program obsługi wyjątków skojarzony z tym wyjątkiem dla tej funkcji. Efekty prologu należy cofnąć, aby obliczyć kontekst funkcji wywołującej. ElementRIPznajduje się w prologu, jeśli odległość od uruchomienia funkcji doRIPwartości jest mniejsza lub równa rozmiarowi prologu zakodowanego w informacjach unwind. Mechanizm odwijania przeszukuje tablicę kodów odwijania w przód w poszukiwaniu pierwszego wpisu o przesunięciu mniejszym lub równym przesunięciu elementuRIPwzględem początku funkcji, a następnie cofa działanie wszystkich pozostałych elementów w tablicy kodów odwijania. Krok 1 jest następnie powtarzany.
- Przypadek b) Jeśli
Przypadek c) Jeśli element
RIPnie znajduje się w prologu lub epilogu, a funkcja ma program obsługi wyjątków (UNW_FLAG_EHANDLERjest ustawiony), wywoływana jest procedura obsługi specyficzna dla języka. Program obsługi skanuje swoje dane i wywołuje odpowiednie funkcje filtrowania. Program obsługi specyficzny dla języka może zwrócić, że wyjątek został obsłużony lub że wyszukiwanie ma być kontynuowane. Może również bezpośrednio zainicjować odwijanie.
Jeśli program obsługi specyficzny dla języka zwraca obsługiwany stan, wykonywanie będzie kontynuowane przy użyciu oryginalnego rekordu kontekstu.
Jeśli nie ma procedury obsługi właściwej dla danego języka lub procedura obsługi zwraca stan „kontynuuj wyszukiwanie”, rekord kontekstu musi zostać przywrócony do stanu wywołującego. Procedura odwijania odwraca działanie każdego elementu w tablicy kodów odwijania. Krok 1 jest następnie powtarzany.
Gdy są zaangażowane informacje o łańcuchu odwijań, te podstawowe kroki są nadal wykonywane. Jedyną różnicą jest to, że podczas przechodzenia przez tablicę kodów odwijania w celu cofnięcia skutków prologu, gdy proces dotrze do końca tablicy, przechodzi do nadrzędnych informacji odwijania i przechodzi przez całą znajdującą się tam tablicę kodów odwijania. To łączenie będzie kontynuowane do momentu uzyskania informacji o odwijeniu bez UNW_CHAINED_INFO flagi, a następnie kończy chodzenie po tablicy kodu odwijania.
Najmniejszy zestaw danych odwijanych to 8 bajtów. Taki zestaw reprezentuje funkcję, która alokuje tylko 128 bajtów stosu lub mniej i prawdopodobnie zapisuje jeden rejestr nieulotny. Jest to również rozmiar struktury informacji o rozwijaniu łańcuchowo dla prologu o zerowej długości bez kodów rozwijania.
Mechanizm obsługi specyficzny dla języka
Struktura UNWIND_INFO udostępnia względny adres procedury obsługi specyficznej dla języka, gdy ustawiono flagę UNW_FLAG_EHANDLER lub UNW_FLAG_UHANDLER. Zgodnie z opisem w poprzedniej sekcji wyszukiwanie programu obsługi wyjątków lub proces odwijania wywołuje procedurę obsługi specyficznej dla języka. Funkcja obsługi ma następujący prototyp:
typedef EXCEPTION_DISPOSITION (*PEXCEPTION_ROUTINE) (
IN PEXCEPTION_RECORD ExceptionRecord,
IN ULONG64 EstablisherFrame,
IN OUT PCONTEXT ContextRecord,
IN OUT PDISPATCHER_CONTEXT DispatcherContext
);
ExceptionRecord dostarcza wskaźnik do rekordu wyjątku, który ma standardową definicję Win64.
EstablisherFrame to podstawowy adres alokacji stałego stosu dla tej funkcji.
ContextRecord wskazuje kontekst wyjątku w momencie zgłoszenia wyjątku (w przypadku procedury obsługi wyjątków) lub bieżącego kontekstu "odwijania" (w przypadku procedury obsługi zakończenia).
DispatcherContext wskazuje kontekst dyspozytora dla tej funkcji. Ma następującą definicję:
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 jest wartością RIP w ramach tej funkcji. Ta wartość jest adresem lokalizacji wyjątku lub adresem, pod którym kontrola opuściła funkcję ustanawiającą. Element RIP służy do określania, czy przepływ sterowania znajduje się w jakiejś konstrukcji chronionej wewnątrz tej funkcji, na przykład w bloku __try dla __try/__except lub __try/__finally.
ImageBase to baza obrazów (adres ładowania) modułu zawierającego tę funkcję. 32-bitowe przesunięcia używane we wpisie funkcji i odwijeniu informacji powinny zostać dodane do bazy obrazów uzyskiwania końcowego adresu.
FunkcjaEntry dostarcza wskaźnik do wpisu funkcji zawierającego RUNTIME_FUNCTION funkcję i odwijanie adresów względnych bazy obrazów dla tej funkcji.
EstablisherFrame to podstawowy adres alokacji stałego stosu dla tej funkcji.
TargetIp dostarcza opcjonalny adres instrukcji, który określa adres kontynuacji odwijenia. Ten adres jest ignorowany, jeśli element EstablisherFrame nie jest określony.
ContextRecord wskazuje kontekst wyjątku, używany przez systemowy kod obsługi/odwijania wyjątków.
LanguageHandler wskazuje wywoływaną specyficzną procedurę obsługi języka.
HandlerData wskazuje dane obsługi specyficzne dla języka dla tej funkcji.
Odwijanie pomocników dla rozwiązania MASM
Aby pisać poprawne procedury w asemblerze, należy używać zestawu pseudodyrektyw obok właściwych instrukcji asemblera. Te pseudo-operacje tworzą odpowiednie .pdata i .xdata. Ponadto należy użyć zestawu makr, które upraszczają korzystanie z tych pseudooperacyjności w ich najbardziej typowych zastosowaniach.
Surowe pseudo-operacje
| Pseudo operacja | opis |
|---|---|
| PROC FRAME [:ehandler] | Powoduje, że MASM generuje wpis tabeli funkcji w .pdata oraz informacje rozwijania stosu w .xdata dotyczące zachowania rozwijania stosu dla obsługi wyjątków strukturalnych funkcji. Jeśli program obsługi ehandler jest obecny, ta procedura jest wprowadzana do pliku .xdata jako program obsługi specyficzny dla języka.Jeśli używasz atrybutu FRAME, po nim użyj dyrektywy .ENDPROLOG. Jeśli funkcja jest funkcją liścia (zgodnie z definicją w typach funkcji), atrybut FRAME jest niepotrzebny, podobnie jak reszta tych pseudo-operacji. |
| .PUSHREG rejestr | Generuje wpis kodu odwijanego UWOP_PUSH_NONVOL dla określonego numeru rejestru przy użyciu bieżącego przesunięcia w prologu.Używaj go tylko z nieulotnymi rejestrami liczb całkowitych. W przypadku odkładania rejestrów ulotnych na stos użyj zamiast tego dyrektywy .ALLOCSTACK 8. |
| .SETFRAME rejestr, przesunięcie | Wypełnia pole rejestru ramki i przesuwa informacje o odwijeniu przy użyciu określonego rejestru i przesunięcia. Przesunięcie musi mieć wielokrotność 16 i mniejsze niż lub równe 240. Ta dyrektywa generuje również wpis kodu unwind UWOP_SET_FPREG dla określonego rejestru z użyciem bieżącego przesunięcia prologu. |
| .ALLOCSTACK rozmiar | Generuje wartość UWOP_ALLOC_SMALL lub UWOP_ALLOC_LARGE o określonym rozmiarze dla bieżącego przesunięcia w prologu.Operand rozmiaru musi mieć wielokrotność 8. |
| .SAVEREG rejestr, przesunięcie | Generuje wpis kodu odwijania UWOP_SAVE_NONVOL lub UWOP_SAVE_NONVOL_FAR dla określonego rejestru i przesunięcia przy użyciu bieżącego przesunięcia prologu. Rozwiązanie MASM wybiera najbardziej wydajne kodowanie.przesunięcie musi być dodatnie, a wielokrotność 8. przesunięcie jest liczone względem podstawy ramki procedury, która zazwyczaj znajduje się w RSP, lub, jeśli używany jest wskaźnik ramki, względem nieskalowanego wskaźnika ramki. |
| . SAVEXMM128 rejestr, przesunięcie | Generuje wpis kodu odwijania UWOP_SAVE_XMM128 albo UWOP_SAVE_XMM128_FAR dla określonego rejestru XMM i przesunięcia z użyciem bieżącego przesunięcia prologu. Rozwiązanie MASM wybiera najbardziej wydajne kodowanie.przesunięcie musi być dodatnie i musi być wielokrotnością 16. przesunięcie jest liczone względem bazy ramki procedury, która zwykle znajduje się w RSP, lub — jeśli używany jest wskaźnik ramki — względem nieskalowanego wskaźnika ramki. |
| .PUSHFRAME [kod] | Generuje wpis kodu odwijania UWOP_PUSH_MACHFRAME. Jeśli określisz opcjonalny kod, wpis kodu unwind otrzymuje modyfikator 1. W przeciwnym razie modyfikator ma wartość 0. |
| . ENDPROLOG | Sygnalizuje koniec deklaracji prologu. Musi występować w pierwszych 255 bajtach funkcji. |
Oto przykładowy prolog funkcji z odpowiednim użyciem większości kodów operacyjnych.
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
Aby uzyskać więcej informacji na temat przykładu epilogu, zobacz Epilog code w Prolog i epilog dla x64.
Makra MASM
Aby uprościć korzystanie z surowych pseudooperacji, użyj zestawu makr zdefiniowanych w pliku ksamd64.inc. Te makra ułatwiają tworzenie typowych prologów i epilogów procedur.
| Makro | opis |
|---|---|
| alloc_stack(n) | Przydziela ramkę stosu o rozmiarze n bajtów (przy użyciu sub rsp, n) i generuje odpowiednie informacje o odwijaniu stosu (.allocstack n) |
| save_reg reg, loc | Zapisuje niewolatylny rejestr reg na stosie pod przesunięciem RSPloc i generuje odpowiednie informacje rozwijania stosu (.savereg reg, loc) |
| push_reg reg | Umieszcza nieulotny rejestr reg na stos i emituje odpowiednie informacje rozwijania stosu (.pushreg reg) |
| rex_push_reg reg | Zapisuje nieulotny rejestr na stosie przy użyciu 2-bajtowej instrukcji push i generuje odpowiednie informacje do odwijania stosu (.pushreg reg). Użyj tego makra, jeśli push jest pierwszą instrukcją w funkcji, aby upewnić się, że funkcja jest podatna na gorące łatanie. |
| save_xmm128 reg, loc | Zapisuje nieulotny rejestr XMMreg na stosie pod RSP przesunięciem loc i emituje odpowiednie informacje rozwijania stosu (.savexmm128 reg, loc) |
| set_frame reg, przesunięcie | Ustawia rejestr ramki reg na wartość RSP + przesunięcia (przy użyciu mov lub lea) i generuje odpowiednie informacje unwind (.set_frame reg, offset) |
| push_eflags | Wypycha eflags przy użyciu pushfq instrukcji i emituje odpowiednie informacje odwijania (.alloc_stack 8) |
Oto przykładowy prolog funkcji z odpowiednim użyciem makr:
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
Rozwiń definicje danych w języku C
Oto opis C danych dotyczących odwijania:
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))