Gestionare structurată a excepțiilor

Gestionarea structurată a excepțiilor ( SEH  - Structured Exception Handling ) este un mecanism pentru gestionarea excepțiilor software și hardware în sistemul de operare Microsoft Windows care permite programatorilor să controleze gestionarea excepțiilor și este, de asemenea, un instrument de depanare [1] .

Excepții și gestionarea excepțiilor

O excepție este un eveniment din timpul execuției programului care îl face să se comporte anormal sau incorect. Există două tipuri de excepții: hardware, care sunt generate de procesor și software, generate de sistemul de operare și programele de aplicație . Mecanismul structurat de gestionare a excepțiilor vă permite să gestionați atât excepțiile software, cât și cele hardware în același mod.

Implementare

Cuvinte cheie

Mecanismul este acceptat de Microsoft doar la nivel de compilator prin implementarea constructelor de sintaxă non- __trystandard __exceptși __finally. Cuvântul cheie este __tryfolosit pentru a evidenția o secțiune de cod în care aruncarea unei excepții va fi gestionată de unul sau mai multe blocuri __except. Codul din bloc __finallyva fi întotdeauna executat indiferent de celelalte blocuri __tryși __except[2] .

Exemplu de utilizare în C și C++

__incearca { // cod protejat, // care este plasat într-un cadru SEH } __cu excepția ( filtrul de excepții ) { // handler de excepții } __în sfârșit { // cod să ruleze oricum }

Filtrele de excepție pot fi funcții obișnuite care returnează trei expresii constante: [3]

  • EXCEPTION_EXECUTE_HANDLER - indică capacitatea acestui handler de a gestiona excepția. La primirea unei astfel de valori, sistemul de operare oprește căutarea de gestionare a excepțiilor relevanți și, după derularea stivei, transferă controlul către primul care a returnat EXCEPTION_EXECUTE_HANDLER

  • EXCEPTION_CONTINUE_EXECUTION - indică o remediere a erorilor. Sistemul va transfera din nou controlul instrucțiunii care a lansat excepția, deoarece este de așteptat ca de data aceasta să nu arunce o excepție. [patru]
  • EXCEPTION_CONTINUE_SEARCH - indică faptul că un manipulator potrivit poate fi găsit mai sus în stivă. În același timp, returnarea acestei valori poate indica faptul că eroarea nu a fost tratată. [3]

Structuri și mecanisme utilizate

Fiecare thread din orice proces folosește un registru ( selector de 16 biți ) fspentru a stoca un pointer către o structură de date Thread Information Block care conține informații despre acel fir. Această structură stochează un pointer către ultima structură _EXCEPTION_REGISTRATION_RECORD înregistrată din lista legată , care include un pointer către handlerul de excepții și un pointer către intrarea anterioară _EXCEPTION_REGISTRATION_RECORD . [5] Când este creat un fir de execuție, sistemul de operare adaugă un handler de excepții implicit numit de . kernel32!UnhandledExceptionFilter

Prototipul funcției de gestionare a apelului invers este următorul:

EXCEPTION_DISPOSITION __cdecl _except_handler ( struct _EXCEPTION_RECORD * ExceptionRecord , void * EstablisherFrame , struct_CONTEXT * ContextRecord , _ void * DispatcherContext );

De fiecare dată când programatorul utilizează construcția __try, o nouă instanță a structurii _EXCEPTION_REGISTRATION_RECORD, care indică funcția _except_handler3 a bibliotecii msvcrt.dll , este adăugată la stiva firului de execuție . Codul bloc este apelat de la _except_handler3. La sfârșitul blocului , compilatorul adaugă cod care elimină intrarea curentă _EXCEPTION_REGISTRATION_RECORD și restabilește valoarea pointerului la intrarea anterioară. __except__finally__tryfs:0

Când apare o excepție, sistemul iterează prin întregul lanț de gestionari de întreruperi în secvență. Fiecare handler returnează o valoare care indică dacă poate gestiona această excepție sau nu. Indicatorul către sfârșitul listei de manipulatori de excepții disponibili este valoarea FFFFFFFFsituată pe stivă după ultimul handler. Dacă sistemul găsește operatorul dorit, atunci controlul este transferat acestuia. În același timp, după ce a găsit handler-ul relevant pentru excepția care a apărut, sistemul de operare nu îi transferă imediat controlul, ci din nou apelează succesiv toți handler-ii de-a lungul lanțului cu steag EH_UNWINDINGpentru a curăța (apelați destructorul ) . [4] Dacă niciunul dintre filtrele de gestionare a excepțiilor stabilite de programator nu a returnat EXCEPTION_EXECUTE_HANDLER sau EXCEPTION_CONTINUE_EXECUTION, atunci UnhandledExceptionFilter filtrul implicit de gestionare a excepțiilor, care este înregistrat când firul de execuție se pregătește să ruleze, este executat.

Apel handler

Când apare o excepție, sistemul de operare nu apelează direct filtrul de excepții (care este responsabil pentru faptul că un anumit handler va gestiona excepția care a avut loc sau nu), ci îi transmite adresa funcției _except_handler3, de unde este apelată funcția de filtru. . Utilizează următoarea structură de date: [6]

struct _EXCEPTION_REGISTRATION { struct _EXCEPTION_REGISTRATION * prev ; void ( * handler )( PEXCEPTION_RECORD , PEXCEPTION_REGISTRATION , PCONTEXT , PEXCEPTION_RECORD ); struct scopetable_entry * scopetable ; int trylevel ; int_ebp ; _ PEXCEPTION_POINTERS xpointers ; };

Câmpul *scopetableindică adresa unei matrice de structuri scopetable_entry, iar câmpul întreg la nivel de încercare indică un index din această matrice. Câmpul _ebpconține valoarea indicatorului de cadru de stivă care a existat înainte de crearea structurii EXCEPTION_REGISTRATION. [7] Funcția _except_handler3apelează filtrul necesar și, înainte de a apela handler-ul, derulează (curăță) stiva prin funcția ntdll.dll!RtlUnwind.

Dacă niciunul dintre handlerii instalați de programator nu a fost de acord să gestioneze excepția, atunci este apelată o funcție UnhandledExceptionFiltercare verifică dacă procesul rulează sub depanator și îl informează dacă este disponibil. [7] Funcția apelează apoi filtrul de handler implicit (care este setat de funcție SetUnhandledExceptionFilterși care returnează întotdeauna EXCEPTION_EXECUTE_HANDLER). [7] Apoi, în funcție de setările sistemului de operare, se apelează fie depanatorul, fie funcția NtRaiseHardError, care afișează un mesaj de eroare. [7]

Note

  1. Gestionarea excepțiilor structurate (Windows) . Consultat la 5 mai 2010. Arhivat din original pe 25 septembrie 2010.
  2. Despre gestionarea structurată a excepțiilor (Windows) . Consultat la 5 mai 2010. Arhivat din original pe 28 februarie 2011.
  3. 1 2 Introducere în gestionarea structurată a excepțiilor SEH (link mort) . Data accesului: 26 decembrie 2012. Arhivat din original pe 27 martie 2014. 
  4. 1 2 WASM.IN Win32 SEH În interior (Partea 1) . Consultat la 5 aprilie 2018. Arhivat din original pe 5 aprilie 2018.
  5. Operarea SEH într-un mediu Win32 . Preluat la 1 mai 2010. Arhivat din original la 24 septembrie 2015.
  6. WASM.IN Win32 SEH din interior (partea 2) . Consultat la 5 aprilie 2018. Arhivat din original pe 5 aprilie 2018.
  7. 1 2 3 4 WASM.IN Win32 SEH din interior (partea 3) . Consultat la 5 aprilie 2018. Arhivat din original pe 5 aprilie 2018.