;*** ;exsup3.asm ; ; Copyright (c) 1994 Microsoft Corporation. All rights reserved. ; ;Purpose: ; Exception handling for i386. This is just the C9.0 version of ; the language-specific exception handler. The C8.0 version is ; found in exsup2.asm, and the routines common to both C8 and C9 ; are found in exsup.asm. ; ;Notes: ; ;Revision History: ; 01-10-94 PML Create VC/C++ 2.0 (C9.0) version from C8.0 original ; ;******************************************************************************* ;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 ifdef _WIN32_ _WIN32_OR_POSIX_ equ 1 endif ifdef _POSIX_ _WIN32_OR_POSIX_ equ 1 endif .xlist include pversion.inc ?DFDATA = 1 ?NODATA = 1 include cmacros.mas include exsup.inc .list ;REVIEW: can we get rid of _global_unwind2, and just use ; the C runtimes version, _global_unwind? extrn __global_unwind2:near extrn __local_unwind2:near ;typedef struct _EXCEPTION_REGISTRATION PEXCEPTION_REGISTRATION; ;struct _EXCEPTION_REGISTRATION{ ;/* _esp, xpointers at negative offset */ ; int _esp; ; PEXCEPTION_POINTERS xpointers; ; struct _EXCEPTION_REGISTRATION *prev; ; void (*handler)(PEXCEPTION_RECORD, PEXCEPTION_REGISTRATION, PCONTEXT, PEXCEPTION_RECORD); ; struct scopetable_entry *scopetable; ; int trylevel; ;}; ;private (at negative offsets from node ptr) _esp = -8 xpointers = -4 _C9_EXCEPTION_REGISTRATION struc ; C9.0 version ;public: dd ? ; prev (common) dd ? ; handler (common) ;private: scopetable dd ? trylevel dd ? _C9_EXCEPTION_REGISTRATION ends FRAME_EBP_OFFSET equ 16 ;#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 ;/* following is the structure returned by the _exception_info() intrinsic. */ ;typedef struct _EXCEPTION_POINTERS EXCEPTION_POINTERS; ;typedef struct EXCEPTION_POINTERS *PEXCEPTION_POINTERS; ;struct _EXCEPTION_POINTERS{ ; PEXCEPTION_RECORD ExceptionRecord; ; PCONTEXT Context; ;}; _EXCEPTION_POINTERS struc ep_xrecord dd ? ep_context dd ? _EXCEPTION_POINTERS ends SIZEOF_EXCEPTION_POINTERS equ 8 assumes DS,DATA assumes FS,DATA __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 BeginCODE if @Version LT 600 ifdef _WIN32_OR_POSIX_ ;this needs to go here to work around a masm5 bug. (emits wrong fixup) assumes CS,FLAT else assumes CS,CODE endif endif ;/* _EXCEPT_HANDLER3 - Try to find an exception handler listed in the scope ; * table associated with the given registration record, that wants to accept ; * the current exception. If we find one, run it (and never return). ; * RETURNS: (*if* it returns) ; * DISPOSITION_DISMISS - dismiss the exception. ; * DISPOSITION_CONTINUE_SEARCH - pass the exception up to enclosing handlers ; */ ;int _except_handler3( ; PEXCEPTION_RECORD exception_record, ; PEXCEPTION_REGISTRATION registration, ; PCONTEXT context, ; PEXCEPTION_REGISTRATION dispatcher) ;{ ; int ix, filter_result; ; ; for(ix=registration->trylevel; ix!=-1; ix=registration->xscope[ix].enclosing_level){ ; /* if filter==NULL, then this is an entry for a termination handler */ ; if(registration->xscope[ix].filter){ ; /* NB: call to the filter may trash the callee save ; registers. (this is *not* a standard cdecl function) */ ; filter_result=(*registration->xscope[ix].filter)(xinfo); ; if(filter_result==FILTER_DISMISS) ; return(-1); /* dismiss */ ; if(filter_result==FILTER_ACCEPT){ ; _global_unwind2(registration); ; _local_unwind2(registration, ix); ; (*registration->xscope[ix].specific_handler)(void); ; assert(UNREACHED); /*should never return from handler*/ ; } ; assert(filter_result==FILTER_CONTINUE_SEARCH); ; } ; } ; return(0); /* didnt find one */ ;} db 'VC20' ;; VC/C++ 2.0/32-bit (C9.0) version db 'XC00' ;; so debugger can recognize this proc (cuda:3936) cProc _except_handler3,, parmDP xrecord parmDP registration parmDP context parmDP dispatcher localV xp,SIZEOF_EXCEPTION_POINTERS cBegin ;4*4b for callee saves + 4b return address + 4b param = 24 ;DF in indeterminate state at time of exception, so clear it cld mov ebx, registration ;ebx= PEXCEPTION_REGISTRATION mov eax, xrecord test [eax.exception_flags], EXCEPTION_UNWIND_CONTEXT jnz short _lh_unwinding ;build the EXCEPTION_POINTERS locally store its address in the ; registration record. this is the pointer that is returned by ; the _eception_info intrinsic. mov xp.ep_xrecord, eax mov eax, context mov xp.ep_context, eax lea eax, xp mov [ebx.xpointers], eax mov esi, [ebx.trylevel] ;esi= try level mov edi, [ebx.scopetable] ;edi= scope table base _lh_top: cmp esi, -1 je short _lh_bagit lea ecx, [esi+esi*2] ;ecx= trylevel*3 cmp dword ptr [(edi+ecx*4).filter], 0 je short _lh_continue ;term handler, so keep looking ;filter may trash *all* registers, so save ebp and scopetable offset push esi push ebp lea ebp, FRAME_EBP_OFFSET[ebx] call [(edi+ecx*4).filter] ;call the filter pop ebp pop esi ;ebx may have been trashed by the filter, so we must reload mov ebx, registration ; Accept <0, 0, >0 instead of just -1, 0, +1 or eax, eax jz short _lh_continue js short _lh_dismiss ;assert(eax==FILTER_ACCEPT) ;reload xscope base, cuz it was trashed by the filter call mov edi, [ebx.scopetable] ;load handler address before we loose components of address mode push ebx ;registration* call __global_unwind2 ;run term handlers add esp, 4 ;setup ebp for the local unwinder and the specific handler lea ebp, FRAME_EBP_OFFSET[ebx] ;the stop try level == accepting except level push esi ;stop try level push ebx ;registration* call __local_unwind2 add esp, 8 lea ecx, [esi+esi*2] ;ecx=trylevel*3 ; set the current trylevel to our enclosing level immediately ; before giving control to the handler. it is the enclosing ; level, if any, that guards the handler. mov eax, [(edi+ecx*4).enclosing_level] mov [ebx.trylevel], eax call [(edi+ecx*4).specific_handler] ;call the except handler ;assert(0) ;(NB! should not return) _lh_continue: ;reload the scope table base, possibly trashed by call to filter mov edi, [ebx.scopetable] lea ecx, [esi+esi*2] mov esi, [edi+ecx*4+0] ;load the enclosing trylevel jmp short _lh_top _lh_dismiss: mov eax, DISPOSITION_DISMISS ;dismiss the exception jmp short _lh_return _lh_bagit: mov eax, DISPOSITION_CONTINUE_SEARCH jmp short _lh_return _lh_unwinding: push ebp lea ebp, FRAME_EBP_OFFSET[ebx] push -1 push ebx call __local_unwind2 add esp, 8 pop ebp ;the return value is not really relevent in an unwind context mov eax, DISPOSITION_CONTINUE_SEARCH _lh_return: cEnd public __seh_longjmp_unwind@4 __seh_longjmp_unwind@4 proc near push ebp mov ecx, 8[esp] mov ebp, [ecx.saved_ebp] mov eax, [ecx.saved_trylevel] push eax mov eax, [ecx.saved_xregistration] push eax call __local_unwind2 add esp, 8 pop ebp ret 4 __seh_longjmp_unwind@4 endp EndCODE END