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 0
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 0
    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 0
        if (Alloca.size)
53 0
            error(null, 0, 0, "cannot mix `core.std.stdlib.alloca()` and exception handling in `%s()`", &funcsym_p.Sident[0]);
54

55 0
        char[13+5+1] name = void;
56 0
        __gshared int tmpnum;
57 0
        sprintf(name.ptr,"_HandlerTable%d",tmpnum++);
58

59 0
        Symbol *s = symbol_name(name.ptr,SCstatic,tstypes[TYint]);
60 0
        symbol_keep(s);
61
        //symbol_debug(s);
62

63 0
        except_fillInEHTable(s);
64

65 0
        outdata(s);                 // output the scope table
66

67 0
        objmod.ehtables(funcsym_p,cast(uint)funcsym_p.Ssize,s);
68
    }
69 0
    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 0
    uint fsize = NPTRSIZE;             // target size of function pointer
94 0
    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 0
    uint GUARD_SIZE;
114 0
    if (config.ehmethod == EHmethod.EH_DM)
115 0
        GUARD_SIZE = (global.params.is64bit ? 3*8 : 5*4);
116 0
    else if (config.ehmethod == EHmethod.EH_WIN32)
117 0
        GUARD_SIZE = 3*4;
118
    else
119 0
        assert(0);
120

121 0
    int sz = 0;
122

123
    // Address of start of function
124 0
    if (config.ehmethod == EHmethod.EH_WIN32)
125
    {
126
        //symbol_debug(funcsym_p);
127 0
        dtb.xoff(funcsym_p,0,TYnptr);
128 0
        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 0
    long spoff = cod3_spoff();
135 0
    dtb.dword(cast(int)spoff);
136 0
    sz += 4;
137

138
    // Offset from start of function to return code
139 0
    dtb.dword(cast(int)retoffset);
140 0
    sz += 4;
141

142
    // First, calculate starting catch offset
143 0
    int guarddim = 0;                               // max dimension of guard[]
144 0
    int ndctors = 0;                                // number of ESCdctor's
145 0
    foreach (b; BlockRange(startblock))
146
    {
147 0
        if (b.BC == BC_try && b.Bscope_index >= guarddim)
148 0
            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 0
        if (usednteh & EHcleanup)
152 0
            for (code *c = b.Bcode; c; c = code_next(c))
153
            {
154 0
                if (c.Iop == (ESCAPE | ESCddtor))
155 0
                    ndctors++;
156
            }
157
    }
158
    //printf("guarddim = %d, ndctors = %d\n", guarddim, ndctors);
159

160 0
    if (config.ehmethod == EHmethod.EH_DM)
161
    {
162 0
        dtb.size(guarddim + ndctors);
163 0
        sz += NPTRSIZE;
164
    }
165

166 0
    uint catchoffset = sz + (guarddim + ndctors) * GUARD_SIZE;
167

168
    // Generate guard[]
169 0
    int i = 0;
170 0
    foreach (b; BlockRange(startblock))
171
    {
172
        //printf("b = %p, b.Btry = %p, b.offset = %x\n", b, b.Btry, b.Boffset);
173 0
        if (b.BC == BC_try)
174
        {
175 0
            assert(b.Bscope_index >= i);
176 0
            if (i < b.Bscope_index)
177 0
            {   int fillsize = (b.Bscope_index - i) * GUARD_SIZE;
178 0
                dtb.nzeros( fillsize);
179 0
                sz += fillsize;
180
            }
181 0
            i = b.Bscope_index + 1;
182

183 0
            int nsucc = b.numSucc();
184

185 0
            if (config.ehmethod == EHmethod.EH_DM)
186
            {
187
            //printf("DHandlerInfo: offset = %x", (int)(b.Boffset - startblock.Boffset));
188 0
            dtb.dword(cast(int)(b.Boffset - startblock.Boffset));    // offset to start of block
189

190
            // Compute ending offset
191 0
            uint endoffset;
192 0
            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 0
                assert(bn);
196 0
                if (bn.Btry == b.Btry)
197 0
                {    endoffset = cast(uint)(bn.Boffset - startblock.Boffset);
198 0
                     break;
199
                }
200
            }
201
            //printf(" endoffset = %x, prev_index = %d\n", endoffset, b.Blast_index);
202 0
            dtb.dword(endoffset);               // offset past end of guarded block
203
            }
204

205 0
            dtb.dword(b.Blast_index);          // parent index
206

207 0
            if (b.jcatchvar)                           // if try-catch
208
            {
209 0
                assert(catchoffset);
210 0
                dtb.dword(catchoffset);
211 0
                dtb.size(0);                  // no finally handler
212

213 0
                catchoffset += NPTRSIZE + (nsucc - 1) * (3 * NPTRSIZE);
214
            }
215
            else                                        // else try-finally
216
            {
217 0
                assert(nsucc == 2);
218 0
                dtb.dword(0);           // no catch offset
219 0
                block *bhandler = b.nthSucc(1);
220 0
                assert(bhandler.BC == BC_finally);
221
                // To successor of BC_finally block
222 0
                bhandler = bhandler.nthSucc(0);
223
                // finally handler address
224 0
                if (config.ehmethod == EHmethod.EH_DM)
225
                {
226 0
                    assert(bhandler.Boffset > startblock.Boffset);
227 0
                    dtb.size(bhandler.Boffset - startblock.Boffset);    // finally handler offset
228
                }
229
                else
230 0
                    dtb.coff(cast(uint)bhandler.Boffset);
231
            }
232 0
            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 0
    if (usednteh & EHcleanup)
242
    {
243 0
        int[STACKINC] stackbuf;
244 0
        int *stack = stackbuf.ptr;
245 0
        int stackmax = STACKINC;
246

247 0
    int scopeindex = guarddim;
248 0
    foreach (b; BlockRange(startblock))
249
    {
250
        /* Set up stack of scope indices
251
         */
252 0
        stack[0] = b.Btry ? b.Btry.Bscope_index : -1;
253 0
        int stacki = 1;
254

255 0
        uint boffset = cast(uint)b.Boffset;
256 0
        for (code *c = b.Bcode; c; c = code_next(c))
257
        {
258 0
            if (c.Iop == (ESCAPE | ESCdctor))
259
            {
260 0
                code *c2 = code_next(c);
261 0
                if (config.ehmethod == EHmethod.EH_WIN32)
262 0
                    nteh_patchindex(c2, scopeindex);
263 0
                if (config.ehmethod == EHmethod.EH_DM)
264 0
                    dtb.dword(cast(int)(boffset - startblock.Boffset)); // guard offset
265
                // Find corresponding ddtor instruction
266 0
                int n = 0;
267 0
                uint eoffset = boffset;
268 0
                uint foffset;
269 0
                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 0
                    if (!c2)
274 0
                        goto Lnodtor;
275

276 0
                    if (c2.Iop == (ESCAPE | ESCddtor))
277
                    {
278 0
                        if (n)
279 0
                            n--;
280
                        else
281
                        {
282 0
                            foffset = eoffset;
283 0
                            code *cf = code_next(c2);
284 0
                            if (config.ehmethod == EHmethod.EH_WIN32)
285
                            {
286 0
                                nteh_patchindex(cf, stack[stacki - 1]);
287 0
                                foffset += calccodsize(cf);
288 0
                                cf = code_next(cf);
289
                            }
290 0
                            foffset += calccodsize(cf);
291 0
                            while (!cf.isJumpOP())
292
                            {
293 0
                                cf = code_next(cf);
294 0
                                foffset += calccodsize(cf);
295
                            }
296
                            // issue 9438
297
                            //cf = code_next(cf);
298
                            //foffset += calccodsize(cf);
299 0
                            if (config.ehmethod == EHmethod.EH_DM)
300 0
                                dtb.dword(cast(int)(eoffset - startblock.Boffset)); // guard offset
301 0
                            break;
302
                        }
303
                    }
304 0
                    else if (c2.Iop == (ESCAPE | ESCdctor))
305
                    {
306 0
                        n++;
307
                    }
308
                    else
309 0
                        eoffset += calccodsize(c2);
310
                }
311
                //printf("boffset = %x, eoffset = %x, foffset = %x\n", boffset, eoffset, foffset);
312 0
                dtb.dword(stack[stacki - 1]);   // parent index
313 0
                dtb.dword(0);           // no catch offset
314 0
                if (config.ehmethod == EHmethod.EH_DM)
315
                {
316 0
                    assert(foffset > startblock.Boffset);
317 0
                    dtb.size(foffset - startblock.Boffset);    // finally handler offset
318
                }
319
                else
320 0
                    dtb.coff(foffset);  // finally handler address
321 0
                if (stacki == stackmax)
322
                {   // stack[] is out of space; enlarge it
323 0
                    int *pi = cast(int *)Mem.check(malloc((stackmax + STACKINC) * int.sizeof));
324 0
                    memcpy(pi, stack, stackmax * int.sizeof);
325 0
                    if (stack != stackbuf.ptr)
326 0
                        free(stack);
327 0
                    stack = pi;
328 0
                    stackmax += STACKINC;
329
                }
330 0
                stack[stacki++] = scopeindex;
331 0
                ++scopeindex;
332 0
                sz += GUARD_SIZE;
333
            }
334 0
            else if (c.Iop == (ESCAPE | ESCddtor))
335
            {
336 0
                stacki--;
337 0
                assert(stacki != 0);
338
            }
339
        Lnodtor:
340 0
            boffset += calccodsize(c);
341
        }
342
    }
343 0
        if (stack != stackbuf.ptr)
344 0
            free(stack);
345
    }
346

347
    // Generate catch[]
348 0
    foreach (b; BlockRange(startblock))
349
    {
350 0
        if (b.BC == BC_try && b.jcatchvar)         // if try-catch
351
        {
352 0
            int nsucc = b.numSucc();
353 0
            dtb.size(nsucc - 1);           // # of catch blocks
354 0
            sz += NPTRSIZE;
355

356 0
            for (int j = 1; j < nsucc; ++j)
357
            {
358 0
                block *bcatch = b.nthSucc(j);
359

360 0
                dtb.xoff(bcatch.Bcatchtype,0,TYnptr);
361

362 0
                dtb.size(cod3_bpoffset(b.jcatchvar));     // EBP offset
363

364
                // catch handler address
365 0
                if (config.ehmethod == EHmethod.EH_DM)
366
                {
367 0
                    assert(bcatch.Boffset > startblock.Boffset);
368 0
                    dtb.size(bcatch.Boffset - startblock.Boffset);  // catch handler offset
369
                }
370
                else
371 0
                    dtb.coff(cast(uint)bcatch.Boffset);
372

373 0
                sz += 3 * NPTRSIZE;
374
            }
375
        }
376
    }
377 0
    assert(sz != 0);
378 0
    s.Sdt = dtb.finish();
379
}

Read our documentation on viewing source code .

Loading