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