303 lines
8.9 KiB
NASM
303 lines
8.9 KiB
NASM
;***
|
|
;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,<C,PUBLIC>,<IBX,ISI,IDI,IBP>
|
|
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,<C>
|
|
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,<C,PUBLIC>
|
|
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,<C,PUBLIC>
|
|
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
|