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