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