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