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