;*** ;exsup.asm ; ; Copyright (c) 1993-2001, Microsoft Corporation. All rights reserved. ; ;Purpose: ; Exception handling for i386. This file contains those routines ; common to both C8.0 and C9.0. ; ;Notes: ; ;Revision History: ; 04-13-93 JWM setjmp(), longjmp() & raisex() moved to setjmp.asm; ; common data definitions moved to exsup.inc. ; 10-18-93 GJF Ensure direction flag is clear in _except_handler2 ; 12-16-93 PML Accept <0,0,>0 from except filter, not just -1,0,+1 ; 01-10-94 PML Moved C8-specific __except_handler2 to exsup2.inc. ; Only C8/C9 common routines left here. ; 01-20-94 GJF Gave _EXCEPTION_REGISTRATION a _COMMON suffix (fix ; from SteveWo). ; 02-10-94 GJF -1 is the end-of-exception-handler chain marker, not 0. ; 01-11-95 SKS Remove MASM 5.X support ; 04-18-95 JWM Added NLG support ; 04-21-95 JWM NLG routines moved from setjmp.asm, NLG data from ; frame.cpp. ; 04-25-95 JWM Added __NLG_Return2 label. ; 06-07-95 JWM NLG now multithread safe. ; 06-20-95 JWM dwCode passed on stack (11803). ; 07-11-95 JWM unwanted prologue removed from NLG_Notify (11803). ; 07-21-95 JWM Added new entry point, _NLG_Notify1 (16585). ; 03-09-01 PML Add FPO directives for proper callstacks (vs7#221754) ; ;******************************************************************************* ;hnt = -D_WIN32 -Dsmall32 -Dflat32 -Mx $this; ;Define small32 and flat32 since these are not defined in the NT build process small32 equ 1 flat32 equ 1 .xlist include pversion.inc ?DFDATA = 1 ?NODATA = 1 include cmacros.inc include exsup.inc .list ;REVIEW: can we get rid of _global_unwind2, and just use ; the C runtimes version, _global_unwind? ifndef _BUILD_DLL_LIB_ extrn _RtlUnwind@16:near endif ; _BUILD_DLL_LIB_ ;typedef struct _EXCEPTION_REGISTRATION PEXCEPTION_REGISTRATION; ;struct _EXCEPTION_REGISTRATION{ ; struct _EXCEPTION_REGISTRATION *prev; ; void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_REGISTRATION, PCONTEXT, PEXCEPTION_RECORD); ; struct scopetable_entry *scopetable; ; int trylevel; ;}; _EXCEPTION_REGISTRATION_COMMON struc ; C8.0/C9.0 common only dd ? ; prev (OS-req, def'd in exsup.inc) dd ? ; handler (ditto) ;private: scopetable dd ? ; C8/C9 common trylevel dd ? ; C8/C9 common _EXCEPTION_REGISTRATION_COMMON ends ;#define EXCEPTION_MAXIMUM_PARAMETERS 4 ;typedef struct _EXCEPTION_RECORD EXCEPTION_RECORD; ;typedef EXCEPTION_RECORD *PEXCEPTION_RECORD; ;struct _EXCEPTION_RECORD{ ; NTSTATUS ExceptionCode; ; ULONG ExceptionFlags; ; struct _EXCEPTION_RECORD *ExceptionRecord; ; PVOID ExceptionAddress; ; ULONG NumberParameters; ; ULONG ExceptionInformation[EXCEPTION_MAXIMUM_PARAMETERS]; ;}; _EXCEPTION_RECORD struc exception_number dd ? exception_flags dd ? exception_record dd ? exception_address dd ? number_parameters dd ? exception_information dd 4 dup(?) _EXCEPTION_RECORD ends SIZEOF_EXCEPTION_RECORD equ 36 assumes DS,DATA assumes FS,DATA public __except_list __except_list equ 0 ;struct _SCOPETABLE_ENTRY{ ; int enclosing_level; /* lexical level of enclosing scope */ ; int (*filter)(PEXCEPTION_RECORD); /* NULL for a termination handler */ ; void (*specific_handler)(void); /* xcpt or termination handler */ ;}; ;struct _SCOPETABLE_ENTRY Scopetable[NUMTRYS]; _SCOPETABLE_ENTRY struc enclosing_level dd ? filter dd ? specific_handler dd ? _SCOPETABLE_ENTRY ends BeginDATA __NLG_Destination _NLG_INFO <> PUBLIC __NLG_Destination EndDATA BeginCODE ifndef _BUILD_DLL_LIB_ ;NB: call to RtlUnwind appears to trash ebx! and possibly others so just ; to be safe, we save all callee save regs. cProc _global_unwind2,, parmDP stop cBegin push 0 ; ReturnValue push 0 ; ExceptionRecord push offset flat:_gu_return ; TargetIp push stop ; TargetFrame call _RtlUnwind@16 _gu_return: cEnd endif ; _BUILD_DLL_LIB_ ;_unwind_handler( ; PEXCEPTION_RECORD xr, ; PREGISTRATION_RECORD establisher, ; PCONTEXT context, ; PREGISTRATION_RECORD dispatcher); ; ;this is a special purpose handler used to guard our local unwinder. ; its job is to catch collided unwinds. ; ;NB: this code is basically stolen from the NT routine xcptmisc.asm ; and is basically the same method used by the system unwinder (RtlUnwind). ; cProc _unwind_handler, cBegin mov ecx, dword ptr [esp+4] test dword ptr [ecx.exception_flags], EXCEPTION_UNWIND_CONTEXT mov eax, DISPOSITION_CONTINUE_SEARCH jz short _uh_return ; We collide in a _local_unwind. We set the dispatched to the ; establisher just before the local handler so we can unwind ; any future local handlers. mov eax, [esp+8] ; Our establisher is the one ; in front of the local one mov edx, [esp+16] mov [edx], eax ; set dispatcher to local_unwind2 mov eax, DISPOSITION_COLLIDED_UNWIND _uh_return: cEnd ;/* _LOCAL_UNWIND2 - run all termination handlers listed in the scope table ; * associated with the given registration record, from the current lexical ; * level through enclosing levels up to, but not including the given 'stop' ; * level. ; */ ;void _local_unwind2(PEXCEPTION_REGISTRATION xr, int stop) ;{ ; int ix; ; ; for(ix=xr->trylevel; ix!=-1 && ix!=stop; ix=xr->xscope[i].enclosing_level){ ; /* NULL indicates that this entry is for a termination handler */ ; if(xr->xscope[i].filter==NULL){ ; /* NB: call to the termination handler may trash callee save regs */ ; (*xr->xscope[i].specific_handler)(); ; } ; } ; xr->trylevel=stop; ;} ;/* NOTE: frame (ebp) is setup by caller of __local_unwind2 */ PUBLIC __NLG_Return2 cProc _local_unwind2, cBegin .FPO (0,2,3,3,0,0) push ebx push esi push edi ;call to the handler may trash, so we must save it mov eax, [esp+16] ; (eax) = PEXCEPTION_REGISTRATION ;link in a handler to guard our unwind push eax push TRYLEVEL_INVALID push OFFSET FLAT:__unwind_handler push fs:__except_list mov fs:__except_list, esp _lu_top: mov eax, [esp+32] ; (eax) = PEXCEPTION_REGISTRATION mov ebx, [eax.scopetable] mov esi, [eax.trylevel] cmp esi, -1 ; REVIEW: do we need this extra check? je short _lu_done cmp esi, [esp+36] je short _lu_done lea esi, [esi+esi*2] ; esi*= 3 mov ecx, [(ebx+esi*4).enclosing_level] mov [esp+8], ecx ; save enclosing level mov [eax.trylevel], ecx cmp dword ptr [(ebx+esi*4).filter], 0 jnz short _lu_continue push 0101h mov eax, [(ebx+esi*4).specific_handler] call _NLG_Notify call [(ebx+esi*4).specific_handler] __NLG_Return2:: _lu_continue: jmp short _lu_top _lu_done: pop fs:__except_list add esp, 4*3 ; cleanup stack pop edi ; restore c-runtime registers pop esi pop ebx cEnd ;/* _ABNORMAL_TERMINATION - return TRUE if __finally clause entered via ; * _local_unwind2. ; */ ;BOOLEAN _abnormal_termination(void); cProc _abnormal_termination, cBegin .FPO (0,0,0,0,0,0) xor eax, eax ; assume FALSE mov ecx, fs:__except_list cmp [ecx.handler], offset FLAT:__unwind_handler jne short _at_done ; UnwindHandler first? mov edx, [ecx+12] ; establisher of local_unwind2 mov edx, [edx.trylevel] ; is trylevel the same as the cmp [ecx+8], edx ; local_unwind level? jne short _at_done ; no - then FALSE mov eax, 1 ; currently in _abnormal_termination _at_done: cEnd ; ; NLG entrypoints, for debugger support ; On entry: address of non-local goto in eax ; public __NLG_Dispatch OPTION PROLOGUE:NONE OPTION EPILOGUE:NONE _NLG_Notify1 PROC C PUBLIC push ebx push ecx mov ebx, OFFSET __NLG_Destination jmp __NLG_go ; ecx is already set _NLG_Notify1 ENDP _NLG_Notify PROC C PUBLIC, dwInCode:DWORD push ebx push ecx mov ebx, OFFSET __NLG_Destination mov ecx, dwInCode __NLG_Go: mov [ebx.dwCode], ecx mov [ebx.uoffDestination], eax mov [ebx.uoffFramePointer], ebp __NLG_Dispatch:: pop ecx pop ebx ret 4 _NLG_Notify ENDP OPTION PROLOGUE:PROLOGUEDEF OPTION EPILOGUE:EPILOGUEDEF EndCODE END