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 }