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