1 /**
2  * Support for NT exception handling
3  *
4  * Compiler implementation of the
5  * $(LINK2 https://www.dlang.org, D programming language).
6  *
7  * Copyright:   Copyright (C) 1994-1998 by Symantec
8  *              Copyright (C) 2000-2023 by The D Language Foundation, All Rights Reserved
9  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
10  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
11  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/nteh.d, backend/nteh.d)
12  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/backend/nteh.d
13  */
14 
15 module dmd.backend.nteh;
16 
17 import core.stdc.stdio;
18 import core.stdc.string;
19 
20 import dmd.backend.cc;
21 import dmd.backend.cdef;
22 import dmd.backend.code;
23 import dmd.backend.code_x86;
24 import dmd.backend.codebuilder : CodeBuilder;
25 import dmd.backend.dt;
26 import dmd.backend.el;
27 import dmd.backend.global;
28 import dmd.backend.oper;
29 import dmd.backend.rtlsym;
30 import dmd.backend.symtab;
31 import dmd.backend.ty;
32 import dmd.backend.type;
33 
34 version (SCPP)
35 {
36     import scopeh;
37 }
38 else version (HTOD)
39 {
40     import scopeh;
41 }
42 
43 static if (NTEXCEPTIONS)
44 {
45 
46 extern (C++):
47 
48 nothrow:
49 @safe:
50 
51 int REGSIZE();
52 void except_fillInEHTable(Symbol *s);
53 
54 private __gshared
55 {
56     Symbol *s_table;
57     Symbol *s_context;
58     const(char)* s_name_context_tag = "__nt_context";
59     const(char)* s_name_context = "__context";
60     const(char)* s_name_ecode = "__ecode";
61 
62     const(char)* text_nt =
63     "struct __nt_context {" ~
64         "int esp; int info; int prev; int handler; int stable; int sindex; int ebp;" ~
65      "};\n";
66 }
67 
68 // member stable is not used for MARS or C++
69 
70 int nteh_EBPoffset_sindex()     { return -4; }
71 int nteh_EBPoffset_prev()       { return -nteh_contextsym_size() + 8; }
72 int nteh_EBPoffset_info()       { return -nteh_contextsym_size() + 4; }
73 int nteh_EBPoffset_esp()        { return -nteh_contextsym_size() + 0; }
74 
75 int nteh_offset_sindex()        { version (MARS) { return 16; } else { return 20; } }
76 int nteh_offset_sindex_seh()    { return 20; }
77 int nteh_offset_info()          { return 4; }
78 
79 /***********************************
80  */
81 
82 @trusted
83 ubyte *nteh_context_string()
84 {
85     if (config.exe == EX_WIN32)
86         return cast(ubyte *)text_nt;
87     else
88         return null;
89 }
90 
91 /*******************************
92  * Get symbol for scope table for current function.
93  * Returns:
94  *      symbol of table
95  */
96 
97 @trusted
98 private Symbol *nteh_scopetable()
99 {
100     Symbol *s;
101     type *t;
102 
103     if (!s_table)
104     {
105         t = type_alloc(TYint);
106         s = symbol_generate(SC.static_,t);
107         s.Sseg = UNKNOWN;
108         symbol_keep(s);
109         s_table = s;
110     }
111     return s_table;
112 }
113 
114 /*************************************
115  */
116 
117 @trusted
118 void nteh_filltables()
119 {
120 version (MARS)
121 {
122     Symbol *s = s_table;
123     symbol_debug(s);
124     except_fillInEHTable(s);
125 }
126 }
127 
128 /****************************
129  * Generate and output scope table.
130  * Not called for NTEH C++ exceptions
131  */
132 
133 @trusted
134 void nteh_gentables(Symbol *sfunc)
135 {
136     Symbol *s = s_table;
137     symbol_debug(s);
138 version (MARS)
139 {
140     //except_fillInEHTable(s);
141 }
142 else
143 {
144     /* NTEH table for C.
145      * The table consists of triples:
146      *  parent index
147      *  filter address
148      *  handler address
149      */
150     uint fsize = 4;             // target size of function pointer
151     auto dtb = DtBuilder(0);
152     int sz = 0;                     // size so far
153 
154     foreach (b; BlockRange(startblock))
155     {
156         if (b.BC == BC_try)
157         {
158             block *bhandler;
159 
160             dtb.dword(b.Blast_index);  // parent index
161 
162             // If try-finally
163             if (b.numSucc() == 2)
164             {
165                 dtb.dword(0);           // filter address
166                 bhandler = b.nthSucc(1);
167                 assert(bhandler.BC == BC_finally);
168                 // To successor of BC_finally block
169                 bhandler = bhandler.nthSucc(0);
170             }
171             else // try-except
172             {
173                 bhandler = b.nthSucc(1);
174                 assert(bhandler.BC == BC_filter);
175                 dtb.coff(bhandler.Boffset);    // filter address
176                 bhandler = b.nthSucc(2);
177                 assert(bhandler.BC == BC_except);
178             }
179             dtb.coff(bhandler.Boffset);        // handler address
180             sz += 4 + fsize * 2;
181         }
182     }
183     assert(sz != 0);
184     s.Sdt = dtb.finish();
185 }
186 
187     outdata(s);                 // output the scope table
188 version (MARS)
189 {
190     nteh_framehandler(sfunc, s);
191 }
192     s_table = null;
193 }
194 
195 /**************************
196  * Declare frame variables.
197  */
198 
199 @trusted
200 void nteh_declarvars(Blockx *bx)
201 {
202     Symbol *s;
203 
204     //printf("nteh_declarvars()\n");
205 version (MARS)
206 {
207     if (!(bx.funcsym.Sfunc.Fflags3 & Fnteh)) // if haven't already done it
208     {   bx.funcsym.Sfunc.Fflags3 |= Fnteh;
209         s = symbol_name(s_name_context[0 .. strlen(s_name_context)],SC.bprel,tstypes[TYint]);
210         s.Soffset = -5 * 4;            // -6 * 4 for C __try, __except, __finally
211         s.Sflags |= SFLfree | SFLnodebug;
212         type_setty(&s.Stype,mTYvolatile | TYint);
213         symbol_add(s);
214         bx.context = s;
215     }
216 }
217 else
218 {
219     if (!(funcsym_p.Sfunc.Fflags3 & Fnteh))   // if haven't already done it
220     {   funcsym_p.Sfunc.Fflags3 |= Fnteh;
221         if (!s_context)
222             s_context = scope_search(s_name_context_tag, CPP ? SCTglobal : SCTglobaltag);
223         symbol_debug(s_context);
224 
225         s = symbol_name(s_name_context[0 .. strlen(s_name_context)],SC.bprel,s_context.Stype);
226         s.Soffset = -6 * 4;            // -5 * 4 for C++
227         s.Sflags |= SFLfree;
228         symbol_add(s);
229         type_setty(&s.Stype,mTYvolatile | TYstruct);
230 
231         s = symbol_name(s_name_ecode[0 .. strlen(s_name_context)],SC.auto_,type_alloc(mTYvolatile | TYint));
232         s.Sflags |= SFLfree;
233         symbol_add(s);
234     }
235 }
236 }
237 
238 /**************************************
239  * Generate elem that sets the context index into the scope table.
240  */
241 
242 version (MARS)
243 {
244 elem *nteh_setScopeTableIndex(Blockx *blx, int scope_index)
245 {
246     elem *e;
247     Symbol *s;
248 
249     s = blx.context;
250     symbol_debug(s);
251     e = el_var(s);
252     e.EV.Voffset = nteh_offset_sindex();
253     return el_bin(OPeq, TYint, e, el_long(TYint, scope_index));
254 }
255 }
256 
257 
258 /**********************************
259  * Return pointer to context symbol.
260  */
261 
262 @trusted
263 Symbol *nteh_contextsym()
264 {
265     for (SYMIDX si = 0; 1; si++)
266     {   assert(si < globsym.length);
267         Symbol* sp = globsym[si];
268         symbol_debug(sp);
269         if (strcmp(sp.Sident.ptr,s_name_context) == 0)
270             return sp;
271     }
272 }
273 
274 /**********************************
275  * Return size of context symbol on stack.
276  */
277 @trusted
278 uint nteh_contextsym_size()
279 {
280     int sz;
281 
282     if (usednteh & NTEH_try)
283     {
284 version (MARS)
285 {
286         sz = 5 * 4;
287 }
288 else version (SCPP)
289 {
290         sz = 6 * 4;
291 }
292 else version (HTOD)
293 {
294         sz = 6 * 4;
295 }
296 else
297         static assert(0);
298     }
299     else if (usednteh & NTEHcpp)
300     {
301         sz = 5 * 4;                     // C++ context record
302     }
303     else if (usednteh & NTEHpassthru)
304     {
305         sz = 1 * 4;
306     }
307     else
308         sz = 0;                         // no context record
309     return sz;
310 }
311 
312 /**********************************
313  * Return pointer to ecode symbol.
314  */
315 
316 @trusted
317 Symbol *nteh_ecodesym()
318 {
319     SYMIDX si;
320     Symbol *sp;
321 
322     for (si = 0; 1; si++)
323     {   assert(si < globsym.length);
324         sp = globsym[si];
325         symbol_debug(sp);
326         if (strcmp(sp.Sident.ptr, s_name_ecode) == 0)
327             return sp;
328     }
329 }
330 
331 /*********************************
332  * Mark EH variables as used so that they don't get optimized away.
333  */
334 
335 void nteh_usevars()
336 {
337 version (SCPP)
338 {
339     // Turn off SFLdead and SFLunambig in Sflags
340     nteh_contextsym().Sflags &= ~(SFLdead | SFLunambig);
341     nteh_contextsym().Sflags |= SFLread;
342     nteh_ecodesym().Sflags   &= ~(SFLdead | SFLunambig);
343     nteh_ecodesym().Sflags   |= SFLread;
344 }
345 else
346 {
347     // Turn off SFLdead and SFLunambig in Sflags
348     nteh_contextsym().Sflags &= ~SFLdead;
349     nteh_contextsym().Sflags |= SFLread;
350 }
351 }
352 
353 /*********************************
354  * Generate NT exception handling function prolog.
355  */
356 
357 @trusted
358 void nteh_prolog(ref CodeBuilder cdb)
359 {
360     code cs;
361 
362     if (usednteh & NTEHpassthru)
363     {
364         /* An sindex value of -2 is a magic value that tells the
365          * stack unwinder to skip this frame.
366          */
367         assert(config.exe & EX_posix);
368         cs.Iop = 0x68;
369         cs.Iflags = 0;
370         cs.Irex = 0;
371         cs.IFL2 = FLconst;
372         cs.IEV2.Vint = -2;
373         cdb.gen(&cs);                           // PUSH -2
374         return;
375     }
376 
377     /* Generate instance of struct __nt_context on stack frame:
378         [  ]                                    // previous ebp already there
379         push    -1                              // sindex
380         mov     EDX,FS:__except_list
381         push    offset FLAT:scope_table         // stable (not for MARS or C++)
382         push    offset FLAT:__except_handler3   // handler
383         push    EDX                             // prev
384         mov     FS:__except_list,ESP
385         sub     ESP,8                           // info, esp for __except support
386      */
387 
388 //    useregs(mAX);                     // What is this for?
389 
390     cs.Iop = 0x68;
391     cs.Iflags = 0;
392     cs.Irex = 0;
393     cs.IFL2 = FLconst;
394     cs.IEV2.Vint = -1;
395     cdb.gen(&cs);                 // PUSH -1
396 
397     version (MARS)
398     {
399         // PUSH &framehandler
400         cs.IFL2 = FLframehandler;
401         nteh_scopetable();
402     }
403     else
404     {
405     if (usednteh & NTEHcpp)
406     {
407         // PUSH &framehandler
408         cs.IFL2 = FLframehandler;
409     }
410     else
411     {
412         // Do stable
413         cs.Iflags |= CFoff;
414         cs.IFL2 = FLextern;
415         cs.IEV2.Vsym = nteh_scopetable();
416         cs.IEV2.Voffset = 0;
417         cdb.gen(&cs);                       // PUSH &scope_table
418 
419         cs.IFL2 = FLextern;
420         cs.IEV2.Vsym = getRtlsym(RTLSYM.EXCEPT_HANDLER3);
421         makeitextern(getRtlsym(RTLSYM.EXCEPT_HANDLER3));
422     }
423     }
424 
425     CodeBuilder cdb2;
426     cdb2.ctor();
427     cdb2.gen(&cs);                          // PUSH &__except_handler3
428 
429     if (config.exe == EX_WIN32)
430     {
431         makeitextern(getRtlsym(RTLSYM.EXCEPT_LIST));
432     static if (0)
433     {
434         cs.Iop = 0xFF;
435         cs.Irm = modregrm(0,6,BPRM);
436         cs.Iflags = CFfs;
437         cs.Irex = 0;
438         cs.IFL1 = FLextern;
439         cs.IEV1.Vsym = getRtlsym(RTLSYM.EXCEPT_LIST);
440         cs.IEV1.Voffset = 0;
441         cdb2.gen(&cs);                             // PUSH FS:__except_list
442     }
443     else
444     {
445         useregs(mDX);
446         cs.Iop = 0x8B;
447         cs.Irm = modregrm(0,DX,BPRM);
448         cs.Iflags = CFfs;
449         cs.Irex = 0;
450         cs.IFL1 = FLextern;
451         cs.IEV1.Vsym = getRtlsym(RTLSYM.EXCEPT_LIST);
452         cs.IEV1.Voffset = 0;
453         cdb.gen(&cs);                            // MOV EDX,FS:__except_list
454 
455         cdb2.gen1(0x50 + DX);                      // PUSH EDX
456     }
457         cs.Iop = 0x89;
458         NEWREG(cs.Irm,SP);
459         cdb2.gen(&cs);                             // MOV FS:__except_list,ESP
460     }
461 
462     cdb.append(cdb2);
463     cod3_stackadj(cdb, 8);
464 }
465 
466 /*********************************
467  * Generate NT exception handling function epilog.
468  */
469 
470 @trusted
471 void nteh_epilog(ref CodeBuilder cdb)
472 {
473     if (config.exe != EX_WIN32)
474         return;
475 
476     /* Generate:
477         mov     ECX,__context[EBP].prev
478         mov     FS:__except_list,ECX
479      */
480     code cs;
481     reg_t reg;
482 
483 version (MARS)
484     reg = CX;
485 else
486     reg = (tybasic(funcsym_p.Stype.Tnext.Tty) == TYvoid) ? AX : CX;
487 
488     useregs(1 << reg);
489 
490     cs.Iop = 0x8B;
491     cs.Irm = modregrm(2,reg,BPRM);
492     cs.Iflags = 0;
493     cs.Irex = 0;
494     cs.IFL1 = FLconst;
495     // EBP offset of __context.prev
496     cs.IEV1.Vint = nteh_EBPoffset_prev();
497     cdb.gen(&cs);
498 
499     cs.Iop = 0x89;
500     cs.Irm = modregrm(0,reg,BPRM);
501     cs.Iflags |= CFfs;
502     cs.IFL1 = FLextern;
503     cs.IEV1.Vsym = getRtlsym(RTLSYM.EXCEPT_LIST);
504     cs.IEV1.Voffset = 0;
505     cdb.gen(&cs);
506 }
507 
508 /**************************
509  * Set/Reset ESP from context.
510  */
511 
512 @trusted
513 void nteh_setsp(ref CodeBuilder cdb, opcode_t op)
514 {
515     code cs;
516     cs.Iop = op;
517     cs.Irm = modregrm(2,SP,BPRM);
518     cs.Iflags = 0;
519     cs.Irex = 0;
520     cs.IFL1 = FLconst;
521     // EBP offset of __context.esp
522     cs.IEV1.Vint = nteh_EBPoffset_esp();
523     cdb.gen(&cs);               // MOV ESP,__context[EBP].esp
524 }
525 
526 /****************************
527  * Put out prolog for BC_filter block.
528  */
529 
530 @trusted
531 void nteh_filter(ref CodeBuilder cdb, block *b)
532 {
533     code cs;
534 
535     assert(b.BC == BC_filter);
536     if (b.Bflags & BFLehcode)          // if referenced __ecode
537     {
538         /* Generate:
539                 mov     EAX,__context[EBP].info
540                 mov     EAX,[EAX]
541                 mov     EAX,[EAX]
542                 mov     __ecode[EBP],EAX
543          */
544 
545         getregs(cdb,mAX);
546 
547         cs.Iop = 0x8B;
548         cs.Irm = modregrm(2,AX,BPRM);
549         cs.Iflags = 0;
550         cs.Irex = 0;
551         cs.IFL1 = FLconst;
552         // EBP offset of __context.info
553         cs.IEV1.Vint = nteh_EBPoffset_info();
554         cdb.gen(&cs);                 // MOV EAX,__context[EBP].info
555 
556         cs.Irm = modregrm(0,AX,0);
557         cdb.gen(&cs);                     // MOV EAX,[EAX]
558         cdb.gen(&cs);                     // MOV EAX,[EAX]
559 
560         cs.Iop = 0x89;
561         cs.Irm = modregrm(2,AX,BPRM);
562         cs.IFL1 = FLauto;
563         cs.IEV1.Vsym = nteh_ecodesym();
564         cs.IEV1.Voffset = 0;
565         cdb.gen(&cs);                     // MOV __ecode[EBP],EAX
566     }
567 }
568 
569 /*******************************
570  * Generate C++ or D frame handler.
571  */
572 
573 void nteh_framehandler(Symbol *sfunc, Symbol *scopetable)
574 {
575     // Generate:
576     //  MOV     EAX,&scope_table
577     //  JMP     __cpp_framehandler
578 
579     if (scopetable)
580     {
581         symbol_debug(scopetable);
582         CodeBuilder cdb;
583         cdb.ctor();
584         cdb.gencs(0xB8+AX,0,FLextern,scopetable);  // MOV EAX,&scope_table
585 
586 version (MARS)
587         cdb.gencs(0xE9,0,FLfunc,getRtlsym(RTLSYM.D_HANDLER));      // JMP _d_framehandler
588 else
589         cdb.gencs(0xE9,0,FLfunc,getRtlsym(RTLSYM.CPP_HANDLER));    // JMP __cpp_framehandler
590 
591         code *c = cdb.finish();
592         pinholeopt(c,null);
593         codout(sfunc.Sseg,c,null);
594         code_free(c);
595     }
596 }
597 
598 /*********************************
599  * Generate code to set scope index.
600  */
601 
602 code *nteh_patchindex(code* c, int sindex)
603 {
604     c.IEV2.Vsize_t = sindex;
605     return c;
606 }
607 
608 @trusted
609 void nteh_gensindex(ref CodeBuilder cdb, int sindex)
610 {
611     if (!(config.ehmethod == EHmethod.EH_WIN32 || config.ehmethod == EHmethod.EH_SEH) || funcsym_p.Sfunc.Fflags3 & Feh_none)
612         return;
613     // Generate:
614     //  MOV     -4[EBP],sindex
615 
616     cdb.genc(0xC7,modregrm(1,0,BP),FLconst,cast(targ_uns)nteh_EBPoffset_sindex(),FLconst,sindex); // 7 bytes long
617     cdb.last().Iflags |= CFvolatile;
618 
619     //assert(GENSINDEXSIZE == calccodsize(c));
620 }
621 
622 /*********************************
623  * Generate code for setjmp().
624  */
625 
626 @trusted
627 void cdsetjmp(ref CodeBuilder cdb, elem *e,regm_t *pretregs)
628 {
629     code cs;
630     regm_t retregs;
631     uint stackpushsave;
632     uint flag;
633 
634     stackpushsave = stackpush;
635 version (SCPP)
636 {
637     if (CPP && (funcsym_p.Sfunc.Fflags3 & Fcppeh || usednteh & NTEHcpp))
638     {
639         /*  If in C++ try block
640             If the frame that is calling setjmp has a try,catch block then
641             the call to setjmp3 is as follows:
642               __setjmp3(environment,3,__cpp_longjmp_unwind,trylevel,funcdata);
643 
644             __cpp_longjmp_unwind is a routine in the RTL. This is a
645             stdcall routine that will deal with unwinding for CPP Frames.
646             trylevel is the value that gets incremented at each catch,
647             constructor invocation.
648             funcdata is the same value that you put into EAX prior to
649             cppframehandler getting called.
650          */
651         Symbol *s;
652 
653         s = except_gensym();
654         if (!s)
655             goto L1;
656 
657         cdb.gencs(0x68,0,FLextern,s);                 // PUSH &scope_table
658         stackpush += 4;
659         cdb.genadjesp(4);
660 
661         cdb.genc1(0xFF,modregrm(1,6,BP),FLconst,cast(targ_uns)-4);
662                                                 // PUSH trylevel
663         stackpush += 4;
664         cdb.genadjesp(4);
665 
666         cs.Iop = 0x68;
667         cs.Iflags = CFoff;
668         cs.Irex = 0;
669         cs.IFL2 = FLextern;
670         cs.IEV2.Vsym = getRtlsym(RTLSYM.CPP_LONGJMP);
671         cs.IEV2.Voffset = 0;
672         cdb.gen(&cs);                         // PUSH &_cpp_longjmp_unwind
673         stackpush += 4;
674         cdb.genadjesp(4);
675 
676         flag = 3;
677         goto L2;
678     }
679 }
680     if (funcsym_p.Sfunc.Fflags3 & Fnteh)
681     {
682         /*  If in NT SEH try block
683             If the frame that is calling setjmp has a try, except block
684             then the call to setjmp3 is as follows:
685               __setjmp3(environment,2,__seh_longjmp_unwind,trylevel);
686             __seth_longjmp_unwind is supplied by the RTL and is a stdcall
687             function. It is the name that MSOFT uses, we should
688             probably use the same one.
689             trylevel is the value that you increment at each try and
690             decrement at the close of the try.  This corresponds to the
691             index field of the ehrec.
692          */
693         int sindex_off;
694 
695         sindex_off = 20;                // offset of __context.sindex
696         cs.Iop = 0xFF;
697         cs.Irm = modregrm(2,6,BPRM);
698         cs.Iflags = 0;
699         cs.Irex = 0;
700         cs.IFL1 = FLbprel;
701         cs.IEV1.Vsym = nteh_contextsym();
702         cs.IEV1.Voffset = sindex_off;
703         cdb.gen(&cs);                 // PUSH scope_index
704         stackpush += 4;
705         cdb.genadjesp(4);
706 
707         cs.Iop = 0x68;
708         cs.Iflags = CFoff;
709         cs.Irex = 0;
710         cs.IFL2 = FLextern;
711         cs.IEV2.Vsym = getRtlsym(RTLSYM.LONGJMP);
712         cs.IEV2.Voffset = 0;
713         cdb.gen(&cs);                 // PUSH &_seh_longjmp_unwind
714         stackpush += 4;
715         cdb.genadjesp(4);
716 
717         flag = 2;
718     }
719     else
720     {
721         /*  If the frame calling setjmp has neither a try..except, nor a
722             try..catch, then call setjmp3 as follows:
723             _setjmp3(environment,0)
724          */
725     L1:
726         flag = 0;
727     }
728 L2:
729     cs.Iop = 0x68;
730     cs.Iflags = 0;
731     cs.Irex = 0;
732     cs.IFL2 = FLconst;
733     cs.IEV2.Vint = flag;
734     cdb.gen(&cs);                     // PUSH flag
735     stackpush += 4;
736     cdb.genadjesp(4);
737 
738     pushParams(cdb,e.EV.E1,REGSIZE, TYnfunc);
739 
740     getregs(cdb,~getRtlsym(RTLSYM.SETJMP3).Sregsaved & (ALLREGS | mES));
741     cdb.gencs(0xE8,0,FLfunc,getRtlsym(RTLSYM.SETJMP3));      // CALL __setjmp3
742 
743     cod3_stackadj(cdb, -(stackpush - stackpushsave));
744     cdb.genadjesp(-(stackpush - stackpushsave));
745 
746     stackpush = stackpushsave;
747     retregs = regmask(e.Ety, TYnfunc);
748     fixresult(cdb,e,retregs,pretregs);
749 }
750 
751 /****************************************
752  * Call _local_unwind(), which means call the __finally blocks until
753  * stop_index is reached.
754  * Params:
755  *      cdb = append generated code to
756  *      saveregs = registers to save across the generated code
757  *      stop_index = index to stop at
758  */
759 
760 @trusted
761 void nteh_unwind(ref CodeBuilder cdb,regm_t saveregs,uint stop_index)
762 {
763     // Shouldn't this always be CX?
764 version (SCPP)
765     const reg_t reg = AX;
766 else
767     const reg_t reg = CX;
768 
769 version (MARS)
770     // https://github.com/dlang/dmd/blob/cdfadf8a18f474e6a1b8352af2541efe3e3467cc/druntime/src/rt/deh_win32.d#L934
771     const local_unwind = RTLSYM.D_LOCAL_UNWIND2;    // __d_local_unwind2()
772 else
773     // dm/src/win32/ehsup.c
774     const local_unwind = RTLSYM.LOCAL_UNWIND2;      // __local_unwind2()
775 
776     const regm_t desregs = (~getRtlsym(local_unwind).Sregsaved & (ALLREGS)) | (1 << reg);
777     CodeBuilder cdbs;
778     cdbs.ctor();
779     CodeBuilder cdbr;
780     cdbr.ctor();
781     gensaverestore(saveregs & desregs,cdbs,cdbr);
782 
783     CodeBuilder cdbx;
784     cdbx.ctor();
785     getregs(cdbx,desregs);
786 
787     code cs;
788     cs.Iop = LEA;
789     cs.Irm = modregrm(2,reg,BPRM);
790     cs.Iflags = 0;
791     cs.Irex = 0;
792     cs.IFL1 = FLconst;
793     // EBP offset of __context.prev
794     cs.IEV1.Vint = nteh_EBPoffset_prev();
795     cdbx.gen(&cs);                             // LEA  ECX,contextsym
796 
797     int nargs = 0;
798 version (SCPP)
799 {
800     const int take_addr = 1;
801     cdbx.genc2(0x68,0,take_addr);                  // PUSH take_addr
802     ++nargs;
803 }
804 
805     cdbx.genc2(0x68,0,stop_index);                 // PUSH stop_index
806     cdbx.gen1(0x50 + reg);                         // PUSH ECX            ; DEstablisherFrame
807     nargs += 2;
808 version (MARS)
809 {
810     cdbx.gencs(0x68,0,FLextern,nteh_scopetable());      // PUSH &scope_table    ; DHandlerTable
811     ++nargs;
812 }
813 
814     cdbx.gencs(0xE8,0,FLfunc,getRtlsym(local_unwind));  // CALL _local_unwind()
815     cod3_stackadj(cdbx, -nargs * 4);
816 
817     cdb.append(cdbs);
818     cdb.append(cdbx);
819     cdb.append(cdbr);
820 }
821 
822 /*************************************************
823  * Set monitor, hook monitor exception handler.
824  */
825 
826 version (MARS)
827 {
828 @trusted
829 void nteh_monitor_prolog(ref CodeBuilder cdb, Symbol *shandle)
830 {
831     /*
832      *  PUSH    handle
833      *  PUSH    offset _d_monitor_handler
834      *  PUSH    FS:__except_list
835      *  MOV     FS:__except_list,ESP
836      *  CALL    _d_monitor_prolog
837      */
838     CodeBuilder cdbx;
839     cdbx.ctor();
840 
841     assert(config.exe == EX_WIN32);    // BUG: figure out how to implement for other EX's
842 
843     if (shandle.Sclass == SC.fastpar)
844     {   assert(shandle.Spreg != DX);
845         assert(shandle.Spreg2 == NOREG);
846         cdbx.gen1(0x50 + shandle.Spreg);   // PUSH shandle
847     }
848     else
849     {
850         // PUSH shandle
851         useregs(mCX);
852         cdbx.genc1(0x8B,modregrm(2,CX,4),FLconst,4 * (1 + needframe) + shandle.Soffset + localsize);
853         cdbx.last().Isib = modregrm(0,4,SP);
854         cdbx.gen1(0x50 + CX);                      // PUSH ECX
855     }
856 
857     Symbol *smh = getRtlsym(RTLSYM.MONITOR_HANDLER);
858     cdbx.gencs(0x68,0,FLextern,smh);             // PUSH offset _d_monitor_handler
859     makeitextern(smh);
860 
861     code cs;
862     useregs(mDX);
863     cs.Iop = 0x8B;
864     cs.Irm = modregrm(0,DX,BPRM);
865     cs.Iflags = CFfs;
866     cs.Irex = 0;
867     cs.IFL1 = FLextern;
868     cs.IEV1.Vsym = getRtlsym(RTLSYM.EXCEPT_LIST);
869     cs.IEV1.Voffset = 0;
870     cdb.gen(&cs);                   // MOV EDX,FS:__except_list
871 
872     cdbx.gen1(0x50 + DX);                  // PUSH EDX
873 
874     Symbol *s = getRtlsym(RTLSYM.MONITOR_PROLOG);
875     regm_t desregs = ~s.Sregsaved & ALLREGS;
876     getregs(cdbx,desregs);
877     cdbx.gencs(0xE8,0,FLfunc,s);       // CALL _d_monitor_prolog
878 
879     cs.Iop = 0x89;
880     NEWREG(cs.Irm,SP);
881     cdbx.gen(&cs);                         // MOV FS:__except_list,ESP
882 
883     cdb.append(cdbx);
884 }
885 
886 }
887 
888 /*************************************************
889  * Release monitor, unhook monitor exception handler.
890  * Input:
891  *      retregs         registers to not destroy
892  */
893 
894 version (MARS)
895 {
896 
897 @trusted
898 void nteh_monitor_epilog(ref CodeBuilder cdb,regm_t retregs)
899 {
900     /*
901      *  CALL    _d_monitor_epilog
902      *  POP     FS:__except_list
903      */
904 
905     assert(config.exe == EX_WIN32);    // BUG: figure out how to implement for other EX's
906 
907     Symbol *s = getRtlsym(RTLSYM.MONITOR_EPILOG);
908     //desregs = ~s.Sregsaved & ALLREGS;
909     regm_t desregs = 0;
910     CodeBuilder cdbs;
911     cdbs.ctor();
912     CodeBuilder cdbr;
913     cdbr.ctor();
914     gensaverestore(retregs& desregs,cdbs,cdbr);
915     cdb.append(cdbs);
916 
917     getregs(cdb,desregs);
918     cdb.gencs(0xE8,0,FLfunc,s);               // CALL __d_monitor_epilog
919 
920     cdb.append(cdbr);
921 
922     code cs;
923     cs.Iop = 0x8F;
924     cs.Irm = modregrm(0,0,BPRM);
925     cs.Iflags = CFfs;
926     cs.Irex = 0;
927     cs.IFL1 = FLextern;
928     cs.IEV1.Vsym = getRtlsym(RTLSYM.EXCEPT_LIST);
929     cs.IEV1.Voffset = 0;
930     cdb.gen(&cs);                       // POP FS:__except_list
931 }
932 
933 }
934 
935 }