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