1 /**
2  * Generate exception handling tables.
3  *
4  * Copyright:   Copyright (C) 1994-1998 by Symantec
5  *              Copyright (C) 2000-2020 by The D Language Foundation, All Rights Reserved
6  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
7  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
8  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/eh.d, _eh.d)
9  * Documentation:  https://dlang.org/phobos/dmd_eh.html
10  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/eh.d
11  */
12 
13 module dmd.eh;
14 
15 import core.stdc.stdio;
16 import core.stdc.stdlib;
17 import core.stdc..string;
18 
19 import dmd.globals;
20 import dmd.errors;
21 
22 import dmd.root.rmem;
23 
24 import dmd.backend.cc;
25 import dmd.backend.cdef;
26 import dmd.backend.code;
27 import dmd.backend.code_x86;
28 import dmd.backend.dt;
29 import dmd.backend.el;
30 import dmd.backend.global;
31 import dmd.backend.obj;
32 import dmd.backend.ty;
33 import dmd.backend.type;
34 
35 extern (C++):
36 
37 // Support for D exception handling
38 
39 package(dmd) @property @nogc nothrow auto NPTRSIZE() { return _tysize[TYnptr]; }
40 
41 /****************************
42  * Generate and output scope table.
43  */
44 
45 Symbol *except_gentables()
46 {
47     //printf("except_gentables()\n");
48     if (config.ehmethod == EHmethod.EH_DM && !(funcsym_p.Sfunc.Fflags3 & Feh_none))
49     {
50         // BUG: alloca() changes the stack size, which is not reflected
51         // in the fixed eh tables.
52         if (Alloca.size)
53             error(null, 0, 0, "cannot mix `core.std.stdlib.alloca()` and exception handling in `%s()`", &funcsym_p.Sident[0]);
54 
55         char[13+5+1] name = void;
56         __gshared int tmpnum;
57         sprintf(name.ptr,"_HandlerTable%d",tmpnum++);
58 
59         Symbol *s = symbol_name(name.ptr,SCstatic,tstypes[TYint]);
60         symbol_keep(s);
61         //symbol_debug(s);
62 
63         except_fillInEHTable(s);
64 
65         outdata(s);                 // output the scope table
66 
67         objmod.ehtables(funcsym_p,cast(uint)funcsym_p.Ssize,s);
68     }
69     return null;
70 }
71 
72 /**********************************************
73  * Initializes the Symbol s with the contents of the exception handler table.
74  */
75 
76 /* This is what the type should be on the target machine, not the host compiler
77  *
78  * struct Guard
79  * {
80  *    if (EHmethod.EH_DM)
81  *    {
82  *        uint offset;            // offset of start of guarded section (Linux)
83  *        uint endoffset;         // ending offset of guarded section (Linux)
84  *    }
85  *    int last_index;             // previous index (enclosing guarded section)
86  *    uint catchoffset;           // offset to catch block from Symbol
87  *    void *finally;              // finally code to execute
88  * }
89  */
90 
91 void except_fillInEHTable(Symbol *s)
92 {
93     uint fsize = NPTRSIZE;             // target size of function pointer
94     auto dtb = DtBuilder(0);
95 
96     /*
97         void*           pointer to start of function (Windows)
98         uint            offset of ESP from EBP
99         uint            offset from start of function to return code
100         uint nguards;           // dimension of guard[] (Linux)
101         Guard guard[];          // sorted such that the enclosing guarded sections come first
102       catchoffset:
103         uint ncatches;          // number of catch blocks
104         {   void *type;         // Symbol representing type
105             uint bpoffset;      // EBP offset of catch variable
106             void *handler;      // catch handler code
107         } catch[];
108      */
109 
110 /* Be careful of this, as we need the sizeof Guard on the target, not
111  * in the compiler.
112  */
113     uint GUARD_SIZE;
114     if (config.ehmethod == EHmethod.EH_DM)
115         GUARD_SIZE = (global.params.is64bit ? 3*8 : 5*4);
116     else if (config.ehmethod == EHmethod.EH_WIN32)
117         GUARD_SIZE = 3*4;
118     else
119         assert(0);
120 
121     int sz = 0;
122 
123     // Address of start of function
124     if (config.ehmethod == EHmethod.EH_WIN32)
125     {
126         //symbol_debug(funcsym_p);
127         dtb.xoff(funcsym_p,0,TYnptr);
128         sz += fsize;
129     }
130 
131     //printf("ehtables: func = %s, offset = x%x, startblock.Boffset = x%x\n", funcsym_p.Sident, funcsym_p.Soffset, startblock.Boffset);
132 
133     // Get offset of ESP from EBP
134     long spoff = cod3_spoff();
135     dtb.dword(cast(int)spoff);
136     sz += 4;
137 
138     // Offset from start of function to return code
139     dtb.dword(cast(int)retoffset);
140     sz += 4;
141 
142     // First, calculate starting catch offset
143     int guarddim = 0;                               // max dimension of guard[]
144     int ndctors = 0;                                // number of ESCdctor's
145     foreach (b; BlockRange(startblock))
146     {
147         if (b.BC == BC_try && b.Bscope_index >= guarddim)
148             guarddim = b.Bscope_index + 1;
149 //      printf("b.BC = %2d, Bscope_index = %2d, last_index = %2d, offset = x%x\n",
150 //              b.BC, b.Bscope_index, b.Blast_index, b.Boffset);
151         if (usednteh & EHcleanup)
152             for (code *c = b.Bcode; c; c = code_next(c))
153             {
154                 if (c.Iop == (ESCAPE | ESCddtor))
155                     ndctors++;
156             }
157     }
158     //printf("guarddim = %d, ndctors = %d\n", guarddim, ndctors);
159 
160     if (config.ehmethod == EHmethod.EH_DM)
161     {
162         dtb.size(guarddim + ndctors);
163         sz += NPTRSIZE;
164     }
165 
166     uint catchoffset = sz + (guarddim + ndctors) * GUARD_SIZE;
167 
168     // Generate guard[]
169     int i = 0;
170     foreach (b; BlockRange(startblock))
171     {
172         //printf("b = %p, b.Btry = %p, b.offset = %x\n", b, b.Btry, b.Boffset);
173         if (b.BC == BC_try)
174         {
175             assert(b.Bscope_index >= i);
176             if (i < b.Bscope_index)
177             {   int fillsize = (b.Bscope_index - i) * GUARD_SIZE;
178                 dtb.nzeros( fillsize);
179                 sz += fillsize;
180             }
181             i = b.Bscope_index + 1;
182 
183             int nsucc = b.numSucc();
184 
185             if (config.ehmethod == EHmethod.EH_DM)
186             {
187             //printf("DHandlerInfo: offset = %x", (int)(b.Boffset - startblock.Boffset));
188             dtb.dword(cast(int)(b.Boffset - startblock.Boffset));    // offset to start of block
189 
190             // Compute ending offset
191             uint endoffset;
192             for (block *bn = b.Bnext; 1; bn = bn.Bnext)
193             {
194                 //printf("\tbn = %p, bn.Btry = %p, bn.offset = %x\n", bn, bn.Btry, bn.Boffset);
195                 assert(bn);
196                 if (bn.Btry == b.Btry)
197                 {    endoffset = cast(uint)(bn.Boffset - startblock.Boffset);
198                      break;
199                 }
200             }
201             //printf(" endoffset = %x, prev_index = %d\n", endoffset, b.Blast_index);
202             dtb.dword(endoffset);               // offset past end of guarded block
203             }
204 
205             dtb.dword(b.Blast_index);          // parent index
206 
207             if (b.jcatchvar)                           // if try-catch
208             {
209                 assert(catchoffset);
210                 dtb.dword(catchoffset);
211                 dtb.size(0);                  // no finally handler
212 
213                 catchoffset += NPTRSIZE + (nsucc - 1) * (3 * NPTRSIZE);
214             }
215             else                                        // else try-finally
216             {
217                 assert(nsucc == 2);
218                 dtb.dword(0);           // no catch offset
219                 block *bhandler = b.nthSucc(1);
220                 assert(bhandler.BC == BC_finally);
221                 // To successor of BC_finally block
222                 bhandler = bhandler.nthSucc(0);
223                 // finally handler address
224                 if (config.ehmethod == EHmethod.EH_DM)
225                 {
226                     assert(bhandler.Boffset > startblock.Boffset);
227                     dtb.size(bhandler.Boffset - startblock.Boffset);    // finally handler offset
228                 }
229                 else
230                     dtb.coff(cast(uint)bhandler.Boffset);
231             }
232             sz += GUARD_SIZE;
233         }
234     }
235 
236     /* Append to guard[] the guard blocks for temporaries that are created and destroyed
237      * within a single expression. These are marked by the special instruction pairs
238      * (ESCAPE | ESCdctor) and (ESCAPE | ESCddtor).
239      */
240     enum STACKINC = 16;
241     if (usednteh & EHcleanup)
242     {
243         int[STACKINC] stackbuf;
244         int *stack = stackbuf.ptr;
245         int stackmax = STACKINC;
246 
247     int scopeindex = guarddim;
248     foreach (b; BlockRange(startblock))
249     {
250         /* Set up stack of scope indices
251          */
252         stack[0] = b.Btry ? b.Btry.Bscope_index : -1;
253         int stacki = 1;
254 
255         uint boffset = cast(uint)b.Boffset;
256         for (code *c = b.Bcode; c; c = code_next(c))
257         {
258             if (c.Iop == (ESCAPE | ESCdctor))
259             {
260                 code *c2 = code_next(c);
261                 if (config.ehmethod == EHmethod.EH_WIN32)
262                     nteh_patchindex(c2, scopeindex);
263                 if (config.ehmethod == EHmethod.EH_DM)
264                     dtb.dword(cast(int)(boffset - startblock.Boffset)); // guard offset
265                 // Find corresponding ddtor instruction
266                 int n = 0;
267                 uint eoffset = boffset;
268                 uint foffset;
269                 for (; 1; c2 = code_next(c2))
270                 {
271                     // https://issues.dlang.org/show_bug.cgi?id=13720
272                     // optimizer might elide the corresponding ddtor
273                     if (!c2)
274                         goto Lnodtor;
275 
276                     if (c2.Iop == (ESCAPE | ESCddtor))
277                     {
278                         if (n)
279                             n--;
280                         else
281                         {
282                             foffset = eoffset;
283                             code *cf = code_next(c2);
284                             if (config.ehmethod == EHmethod.EH_WIN32)
285                             {
286                                 nteh_patchindex(cf, stack[stacki - 1]);
287                                 foffset += calccodsize(cf);
288                                 cf = code_next(cf);
289                             }
290                             foffset += calccodsize(cf);
291                             while (!cf.isJumpOP())
292                             {
293                                 cf = code_next(cf);
294                                 foffset += calccodsize(cf);
295                             }
296                             // issue 9438
297                             //cf = code_next(cf);
298                             //foffset += calccodsize(cf);
299                             if (config.ehmethod == EHmethod.EH_DM)
300                                 dtb.dword(cast(int)(eoffset - startblock.Boffset)); // guard offset
301                             break;
302                         }
303                     }
304                     else if (c2.Iop == (ESCAPE | ESCdctor))
305                     {
306                         n++;
307                     }
308                     else
309                         eoffset += calccodsize(c2);
310                 }
311                 //printf("boffset = %x, eoffset = %x, foffset = %x\n", boffset, eoffset, foffset);
312                 dtb.dword(stack[stacki - 1]);   // parent index
313                 dtb.dword(0);           // no catch offset
314                 if (config.ehmethod == EHmethod.EH_DM)
315                 {
316                     assert(foffset > startblock.Boffset);
317                     dtb.size(foffset - startblock.Boffset);    // finally handler offset
318                 }
319                 else
320                     dtb.coff(foffset);  // finally handler address
321                 if (stacki == stackmax)
322                 {   // stack[] is out of space; enlarge it
323                     int *pi = cast(int *)Mem.check(malloc((stackmax + STACKINC) * int.sizeof));
324                     memcpy(pi, stack, stackmax * int.sizeof);
325                     if (stack != stackbuf.ptr)
326                         free(stack);
327                     stack = pi;
328                     stackmax += STACKINC;
329                 }
330                 stack[stacki++] = scopeindex;
331                 ++scopeindex;
332                 sz += GUARD_SIZE;
333             }
334             else if (c.Iop == (ESCAPE | ESCddtor))
335             {
336                 stacki--;
337                 assert(stacki != 0);
338             }
339         Lnodtor:
340             boffset += calccodsize(c);
341         }
342     }
343         if (stack != stackbuf.ptr)
344             free(stack);
345     }
346 
347     // Generate catch[]
348     foreach (b; BlockRange(startblock))
349     {
350         if (b.BC == BC_try && b.jcatchvar)         // if try-catch
351         {
352             int nsucc = b.numSucc();
353             dtb.size(nsucc - 1);           // # of catch blocks
354             sz += NPTRSIZE;
355 
356             for (int j = 1; j < nsucc; ++j)
357             {
358                 block *bcatch = b.nthSucc(j);
359 
360                 dtb.xoff(bcatch.Bcatchtype,0,TYnptr);
361 
362                 dtb.size(cod3_bpoffset(b.jcatchvar));     // EBP offset
363 
364                 // catch handler address
365                 if (config.ehmethod == EHmethod.EH_DM)
366                 {
367                     assert(bcatch.Boffset > startblock.Boffset);
368                     dtb.size(bcatch.Boffset - startblock.Boffset);  // catch handler offset
369                 }
370                 else
371                     dtb.coff(cast(uint)bcatch.Boffset);
372 
373                 sz += 3 * NPTRSIZE;
374             }
375         }
376     }
377     assert(sz != 0);
378     s.Sdt = dtb.finish();
379 }