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 }