1 /** 2 * Implements LSDA (Language Specific Data Area) table generation 3 * for Dwarf Exception Handling. 4 * 5 * Copyright: Copyright (C) 2015-2023 by The D Language Foundation, All Rights Reserved 6 * Authors: Walter Bright, https://www.digitalmars.com 7 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 8 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/backend/dwarfeh.d, backend/dwarfeh.d) 9 */ 10 11 module dmd.backend.dwarfeh; 12 13 import core.stdc.stdio; 14 import core.stdc.stdlib; 15 import core.stdc.string; 16 17 import dmd.backend.cc; 18 import dmd.backend.cdef; 19 import dmd.backend.code; 20 import dmd.backend.code_x86; 21 22 import dmd.backend.barray : Barray; 23 import dmd.backend.dwarf; 24 import dmd.backend.dwarf2; 25 26 import dmd.common.outbuffer; 27 28 extern (C++): 29 30 nothrow: 31 32 struct DwEhTableEntry 33 { 34 uint start; 35 uint end; // 1 past end 36 uint lpad; // landing pad 37 uint action; // index into Action Table 38 block *bcatch; // catch block data 39 int prev; // index to enclosing entry (-1 for none) 40 } 41 42 alias DwEhTable = Barray!DwEhTableEntry; 43 44 package __gshared DwEhTable dwehtable; 45 46 /**************************** 47 * Generate .gcc_except_table, aka LS 48 * Params: 49 * sfunc = function to generate table for 50 * seg = .gcc_except_table segment 51 * et = buffer to insert table into 52 * scancode = true if there are destructors in the code (i.e. usednteh & EHcleanup) 53 * startoffset = size of function prolog 54 * retoffset = offset from start of function to epilog 55 */ 56 57 void genDwarfEh(Funcsym *sfunc, int seg, OutBuffer *et, bool scancode, uint startoffset, uint retoffset, ref DwEhTable deh) 58 { 59 /* LPstart = encoding of LPbase 60 * LPbase = landing pad base (normally omitted) 61 * TType = encoding of TTbase 62 * TTbase = offset from next byte to past end of Type Table 63 * CallSiteFormat = encoding of fields in Call Site Table 64 * CallSiteTableSize = size in bytes of Call Site Table 65 * Call Site Table[]: 66 * CallSiteStart 67 * CallSiteRange 68 * LandingPad 69 * ActionRecordPtr 70 * Action Table 71 * TypeFilter 72 * NextRecordPtr 73 * Type Table 74 */ 75 76 et.reserve(100); 77 block *startblock = sfunc.Sfunc.Fstartblock; 78 //printf("genDwarfEh: func = %s, offset = x%x, startblock.Boffset = x%x, scancode = %d startoffset=x%x, retoffset=x%x\n", 79 //sfunc.Sident.ptr, cast(int)sfunc.Soffset, cast(int)startblock.Boffset, scancode, startoffset, retoffset); 80 81 static if (0) 82 { 83 WRfunc("genDwarfEH()", funcsym_p, startblock); 84 printf("-------------------------\n"); 85 } 86 87 uint startsize = cast(uint)et.length(); 88 assert((startsize & 3) == 0); // should be aligned 89 90 deh.reset(); 91 OutBuffer atbuf; 92 OutBuffer cstbuf; 93 94 /* Build deh table, and Action Table 95 */ 96 int index = -1; 97 block *bprev = null; 98 // The first entry encompasses the entire function 99 { 100 uint i = cast(uint) deh.length; 101 DwEhTableEntry *d = deh.push(); 102 d.start = cast(uint)(startblock.Boffset + startoffset); 103 d.end = cast(uint)(startblock.Boffset + retoffset); 104 d.lpad = 0; // no cleanup, no catches 105 index = i; 106 } 107 for (block *b = startblock; b; b = b.Bnext) 108 { 109 if (index > 0 && b.Btry == bprev) 110 { 111 DwEhTableEntry *d = &deh[index]; 112 d.end = cast(uint)b.Boffset; 113 index = d.prev; 114 if (bprev) 115 bprev = bprev.Btry; 116 } 117 if (b.BC == BC_try) 118 { 119 uint i = cast(uint) deh.length; 120 DwEhTableEntry *d = deh.push(); 121 d.start = cast(uint)b.Boffset; 122 123 block *bf = b.nthSucc(1); 124 if (bf.BC == BCjcatch) 125 { 126 d.lpad = cast(uint)bf.Boffset; 127 d.bcatch = bf; 128 uint *pat = bf.actionTable; 129 uint length = pat[0]; 130 assert(length); 131 uint offset = -1; 132 for (uint u = length; u; --u) 133 { 134 /* Buy doing depth-first insertion into the Action Table, 135 * we can combine common tails. 136 */ 137 offset = actionTableInsert(&atbuf, pat[u], offset); 138 } 139 d.action = offset + 1; 140 } 141 else 142 d.lpad = cast(uint)bf.nthSucc(0).Boffset; 143 d.prev = index; 144 index = i; 145 bprev = b.Btry; 146 } 147 if (scancode) 148 { 149 uint coffset = cast(uint)b.Boffset; 150 int n = 0; 151 for (code *c = b.Bcode; c; c = code_next(c)) 152 { 153 if (c.Iop == (ESCAPE | ESCdctor)) 154 { 155 uint i = cast(uint) deh.length; 156 DwEhTableEntry *d = deh.push(); 157 d.start = coffset; 158 d.prev = index; 159 index = i; 160 ++n; 161 } 162 163 if (c.Iop == (ESCAPE | ESCddtor)) 164 { 165 assert(n > 0); 166 --n; 167 DwEhTableEntry *d = &deh[index]; 168 d.end = coffset; 169 d.lpad = coffset; 170 index = d.prev; 171 } 172 coffset += calccodsize(c); 173 } 174 assert(n == 0); 175 } 176 } 177 //printf("deh.dim = %d\n", cast(int)deh.dim); 178 179 static if (1) 180 { 181 /* Build Call Site Table 182 * Be sure to not generate empty entries, 183 * and generate nested ranges reflecting the layout in the code. 184 */ 185 assert(deh.length > 0); 186 uint end = deh[0].start; 187 foreach (ref DwEhTableEntry d; deh[]) 188 { 189 if (d.start < d.end) 190 { 191 void WRITE(uint v) 192 { 193 if (config.objfmt == OBJ_ELF) 194 cstbuf.writeuLEB128(v); 195 else 196 cstbuf.write32(v); 197 } 198 199 uint CallSiteStart = cast(uint)(d.start - startblock.Boffset); 200 WRITE(CallSiteStart); 201 uint CallSiteRange = d.end - d.start; 202 WRITE(CallSiteRange); 203 uint LandingPad = cast(uint)(d.lpad ? d.lpad - startblock.Boffset : 0); 204 WRITE(LandingPad); 205 uint ActionTable = d.action; 206 cstbuf.writeuLEB128(ActionTable); 207 //printf("\t%x %x %x %x\n", CallSiteStart, CallSiteRange, LandingPad, ActionTable); 208 } 209 } 210 } 211 else 212 { 213 /* Build Call Site Table 214 * Be sure to not generate empty entries, 215 * and generate multiple entries for one DwEhTableEntry if the latter 216 * is split by nested DwEhTableEntry's. This is based on the (undocumented) 217 * presumption that there may not 218 * be overlapping entries in the Call Site Table. 219 */ 220 assert(deh.dim); 221 uint end = deh.index(0).start; 222 for (uint i = 0; i < deh.dim; ++i) 223 { 224 uint j = i; 225 do 226 { 227 DwEhTableEntry *d = deh.index(j); 228 //printf(" [%d] start=%x end=%x lpad=%x action=%x bcatch=%p prev=%d\n", 229 // j, d.start, d.end, d.lpad, d.action, d.bcatch, d.prev); 230 if (d.start <= end && end < d.end) 231 { 232 uint start = end; 233 uint dend = d.end; 234 if (i + 1 < deh.dim) 235 { 236 DwEhTableEntry *dnext = deh.index(i + 1); 237 if (dnext.start < dend) 238 dend = dnext.start; 239 } 240 if (start < dend) 241 { 242 void writeCallSite(void delegate(uint) WRITE) 243 { 244 uint CallSiteStart = start - startblock.Boffset; 245 WRITE(CallSiteStart); 246 uint CallSiteRange = dend - start; 247 WRITE(CallSiteRange); 248 uint LandingPad = d.lpad - startblock.Boffset; 249 cstbuf.WRITE(LandingPad); 250 uint ActionTable = d.action; 251 WRITE(ActionTable); 252 //printf("\t%x %x %x %x\n", CallSiteStart, CallSiteRange, LandingPad, ActionTable); 253 } 254 255 if (config.objfmt == OBJ_ELF) 256 writeCallSite((uint a) => cstbuf.writeLEB128(a)); 257 else if (config.objfmt == OBJ_MACH) 258 writeCallSite((uint a) => cstbuf.write32(a)); 259 else 260 assert(0); 261 } 262 263 end = dend; 264 } 265 } while (j--); 266 } 267 } 268 269 /* Write LSDT header */ 270 const ubyte LPstart = DW_EH_PE_omit; 271 et.writeByte(LPstart); 272 uint LPbase = 0; 273 if (LPstart != DW_EH_PE_omit) 274 et.writeuLEB128(LPbase); 275 276 const ubyte TType = (config.flags3 & CFG3pic) 277 ? DW_EH_PE_indirect | DW_EH_PE_pcrel | DW_EH_PE_sdata4 278 : DW_EH_PE_absptr | DW_EH_PE_udata4; 279 et.writeByte(TType); 280 281 /* Compute TTbase, which is the sum of: 282 * 1. CallSiteFormat 283 * 2. encoding of CallSiteTableSize 284 * 3. Call Site Table size 285 * 4. Action Table size 286 * 5. 4 byte alignment 287 * 6. Types Table 288 * Iterate until it converges. 289 */ 290 uint TTbase = 1; 291 uint CallSiteTableSize = cast(uint)cstbuf.length(); 292 uint oldTTbase; 293 do 294 { 295 oldTTbase = TTbase; 296 uint start = cast(uint)((et.length() - startsize) + uLEB128size(TTbase)); 297 TTbase = cast(uint)( 298 1 + 299 uLEB128size(CallSiteTableSize) + 300 CallSiteTableSize + 301 atbuf.length()); 302 uint sz = start + TTbase; 303 TTbase += -sz & 3; // align to 4 304 TTbase += sfunc.Sfunc.typesTable.length * 4; 305 } while (TTbase != oldTTbase); 306 307 if (TType != DW_EH_PE_omit) 308 et.writeuLEB128(TTbase); 309 uint TToffset = cast(uint)(TTbase + et.length() - startsize); 310 311 ubyte CallSiteFormat = 0; 312 if (config.objfmt == OBJ_ELF) 313 CallSiteFormat = DW_EH_PE_absptr | DW_EH_PE_uleb128; 314 else if (config.objfmt == OBJ_MACH) 315 CallSiteFormat = DW_EH_PE_absptr | DW_EH_PE_udata4; 316 317 et.writeByte(CallSiteFormat); 318 et.writeuLEB128(CallSiteTableSize); 319 320 321 /* Insert Call Site Table */ 322 et.write(cstbuf[]); 323 324 /* Insert Action Table */ 325 et.write(atbuf[]); 326 327 /* Align to 4 */ 328 for (uint n = (-et.length() & 3); n; --n) 329 et.writeByte(0); 330 331 /* Write out Types Table in reverse */ 332 auto typesTable = sfunc.Sfunc.typesTable[]; 333 for (int i = cast(int)typesTable.length; i--; ) 334 { 335 Symbol *s = typesTable[i]; 336 /* MACHOBJ 64: pcrel 1 length 1 extern 1 RELOC_GOT 337 * 32: [0] address x004c pcrel 0 length 2 value x224 type 4 RELOC_LOCAL_SECTDIFF 338 * [1] address x0000 pcrel 0 length 2 value x160 type 1 RELOC_PAIR 339 */ 340 341 if (config.objfmt == OBJ_ELF) 342 elf_dwarf_reftoident(seg, et.length(), s, 0); 343 else if (config.objfmt == OBJ_MACH) 344 mach_dwarf_reftoident(seg, et.length(), s, 0); 345 } 346 assert(TToffset == et.length() - startsize); 347 } 348 349 350 /**************************** 351 * Insert action (ttindex, offset) in Action Table 352 * if it is not already there. 353 * Params: 354 * atbuf = Action Table 355 * ttindex = Types Table index (1..) 356 * offset = offset of next action, -1 for none 357 * Returns: 358 * offset of inserted action 359 */ 360 int actionTableInsert(OutBuffer *atbuf, int ttindex, int nextoffset) 361 { 362 //printf("actionTableInsert(%d, %d)\n", ttindex, nextoffset); 363 auto p = cast(const(ubyte)[]) (*atbuf)[]; 364 while (p.length) 365 { 366 int offset = cast(int) (atbuf.length - p.length); 367 int TypeFilter = sLEB128(p); 368 int nrpoffset = cast(int) (atbuf.length - p.length); 369 int NextRecordPtr = sLEB128(p); 370 371 if (ttindex == TypeFilter && 372 nextoffset == nrpoffset + NextRecordPtr) 373 return offset; 374 } 375 int offset = cast(int)atbuf.length(); 376 atbuf.writesLEB128(ttindex); 377 if (nextoffset == -1) 378 nextoffset = 0; 379 else 380 nextoffset -= atbuf.length(); 381 atbuf.writesLEB128(nextoffset); 382 return offset; 383 } 384 385 @("action table insert") unittest 386 { 387 OutBuffer atbuf; 388 static immutable int[3] tt1 = [ 1,2,3 ]; 389 static immutable int[1] tt2 = [ 2 ]; 390 391 int offset = -1; 392 for (size_t i = tt1.length; i--; ) 393 { 394 offset = actionTableInsert(&atbuf, tt1[i], offset); 395 } 396 offset = -1; 397 for (size_t i = tt2.length; i--; ) 398 { 399 offset = actionTableInsert(&atbuf, tt2[i], offset); 400 } 401 402 static immutable ubyte[8] result = [ 3,0,2,0x7D,1,0x7D,2,0 ]; 403 //for (int i = 0; i < atbuf.length(); ++i) printf(" %02x\n", atbuf.buf[i]); 404 assert(result.sizeof == atbuf.length()); 405 int r = memcmp(result.ptr, atbuf.buf, atbuf.length()); 406 assert(r == 0); 407 } 408 409 410 /** 411 * Consumes and decode an unsigned LEB128. 412 * 413 * Params: 414 * data = reference to a slice holding the LEB128 to decode. 415 * When this function return, the slice will point past the LEB128. 416 * 417 * Returns: 418 * decoded value 419 * 420 * See_Also: 421 * https://en.wikipedia.org/wiki/LEB128 422 */ 423 private extern(D) uint uLEB128(ref const(ubyte)[] data) 424 { 425 const(ubyte)* q = data.ptr; 426 uint result = 0; 427 uint shift = 0; 428 while (1) 429 { 430 ubyte byte_ = *q++; 431 result |= (byte_ & 0x7F) << shift; 432 if ((byte_ & 0x80) == 0) 433 break; 434 shift += 7; 435 } 436 data = data[q - data.ptr .. $]; 437 return result; 438 } 439 440 /** 441 * Consumes and decode a signed LEB128. 442 * 443 * Params: 444 * data = reference to a slice holding the LEB128 to decode. 445 * When this function return, the slice will point past the LEB128. 446 * 447 * Returns: 448 * decoded value 449 * 450 * See_Also: 451 * https://en.wikipedia.org/wiki/LEB128 452 */ 453 private extern(D) int sLEB128(ref const(ubyte)[] data) 454 { 455 const(ubyte)* q = data.ptr; 456 ubyte byte_; 457 458 int result = 0; 459 uint shift = 0; 460 while (1) 461 { 462 byte_ = *q++; 463 result |= (byte_ & 0x7F) << shift; 464 shift += 7; 465 if ((byte_ & 0x80) == 0) 466 break; 467 } 468 if (shift < result.sizeof * 8 && (byte_ & 0x40)) 469 result |= -(1 << shift); 470 data = data[q - data.ptr .. $]; 471 return result; 472 } 473 474 /****************************** 475 * Determine size of Signed LEB128 encoded value. 476 * Params: 477 * value = value to be encoded 478 * Returns: 479 * length of decoded value 480 * See_Also: 481 * https://en.wikipedia.org/wiki/LEB128 482 */ 483 uint sLEB128size(int value) 484 { 485 uint size = 0; 486 while (1) 487 { 488 ++size; 489 ubyte b = value & 0x40; 490 491 value >>= 7; // arithmetic right shift 492 if (value == 0 && !b || 493 value == -1 && b) 494 { 495 break; 496 } 497 } 498 return size; 499 } 500 501 /****************************** 502 * Determine size of Unsigned LEB128 encoded value. 503 * Params: 504 * value = value to be encoded 505 * Returns: 506 * length of decoded value 507 * See_Also: 508 * https://en.wikipedia.org/wiki/LEB128 509 */ 510 uint uLEB128size(uint value) 511 { 512 uint size = 1; 513 while ((value >>= 7) != 0) 514 ++size; 515 return size; 516 } 517 518 @("LEB128") unittest 519 { 520 OutBuffer buf; 521 522 static immutable int[16] values = 523 [ 524 0, 1, 2, 3, 300, 4000, 50_000, 600_000, 525 -0, -1, -2, -3, -300, -4000, -50_000, -600_000, 526 ]; 527 528 foreach (i; 0..values.length) 529 { 530 const int value = values[i]; 531 532 buf.reset(); 533 buf.writeuLEB128(value); 534 assert(buf.length() == uLEB128size(value)); 535 auto p = cast(const(ubyte)[]) buf[]; 536 int result = uLEB128(p); 537 assert(!p.length); 538 assert(result == value); 539 540 buf.reset(); 541 buf.writesLEB128(value); 542 assert(buf.length() == sLEB128size(value)); 543 p = cast(const(ubyte)[]) buf[]; 544 result = sLEB128(p); 545 assert(!p.length); 546 assert(result == value); 547 } 548 }