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