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 }