summaryrefslogtreecommitdiffstats
path: root/private/crt32/misc/i386/exsup.asm
blob: 8e7043ecad96ae5ea70143031b298c879b9285a5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
;***
;exsup.asm
;
;	Copyright (c) 1993-1994 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.
;	02-10-94  GJF	-1 is the end-of-exception-handler chain marker, not
;			0.
;
;*******************************************************************************

;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?

ifdef _WIN32_OR_POSIX_
extrn _RtlUnwind@16:near
endif

;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)
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

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

ifdef _WIN32_OR_POSIX_          ;{

;NB: call to RtlUnwind appears to trash ebx! and possibly others so just
; to be save, 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

else            ;}{

;/* _GLOBAL_UNWIND2 - */
;void _global_unwind2(PEXCEPTION_REGISTRATION *stop)
;{
;    for(xr=__except_list; xr!=stop; xr=xr->prev){
;       assert(xr!=NULL);
;       /* NOTE: must set ebp to this scopes frame before call */
;       _local_unwind2(xr, -1);
;    }
;}
cProc _global_unwind2,<C,PUBLIC>,<ISI,IDI>
        parmDP  stop
        localV  xr,SIZEOF_EXCEPTION_RECORD
cBegin
        ;build an EXCEPTION_RECORD to pass to language handler for unwinding
        lea     esi, xr
        mov     [esi.exception_number], 0
        mov     [esi.exception_flags], EXCEPTION_UNWIND_CONTEXT
        ;REVIEW: fill in the rest of the struct?

        mov     edi, dword ptr fs:__except_list
_gu_top:
        cmp     edi, stop
        je      short _gu_done
	cmp	edi, -1 		; -1 means no higher-level handler
        je      short _gu_error

        push    edi
        push    esi
        call    [edi.handler]
        add     esp, 8

        mov     edi, [edi.prev]
        mov     dword ptr fs:__except_list, edi
        jmp     short _gu_top

_gu_error:
        ;assert(0);

_gu_done:
cEnd

endif           ;}

;_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 */
cProc _local_unwind2,<C,PUBLIC>
cBegin
        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

        call    [(ebx+esi*4).specific_handler]

_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
        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


EndCODE
END