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