1 /**
2  * Exception handling support for Dwarf-style portable exceptions.
3  *
4  * Copyright: Copyright (c) 2015-2016 by D Language Foundation
5  * License: Distributed under the
6  *      $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7  *    (See accompanying file LICENSE)
8  * Authors: Walter Bright
9  * Source: $(DRUNTIMESRC rt/_dwarfeh.d)
10  */
11 
12 module rt.dwarfeh;
13 
14 // debug = EH_personality;
15 
16 version (Posix):
17 
18 import rt.dmain2: _d_print_throwable;
19 import core.internal.backtrace.unwind;
20 import core.stdc.stdio;
21 import core.stdc.stdlib;
22 
23 /* These are the register numbers for _Unwind_SetGR().
24  * Hints for these can be found by looking at the EH_RETURN_DATA_REGNO macro in
25  * GCC. If you have a native gcc you can try the following:
26  *
27  * #include <stdio.h>
28  *
29  * int main(int argc, char *argv[])
30  * {
31  *     printf("EH_RETURN_DATA_REGNO(0) = %d\n", __builtin_eh_return_data_regno(0));
32  *     printf("EH_RETURN_DATA_REGNO(1) = %d\n", __builtin_eh_return_data_regno(1));
33  *     return 0;
34  * }
35  */
36 version (X86_64)
37 {
38     enum eh_exception_regno = 0;
39     enum eh_selector_regno = 1;
40 }
41 else version (X86)
42 {
43     enum eh_exception_regno = 0;
44     enum eh_selector_regno = 2;
45 }
46 else version (AArch64)
47 {
48     enum eh_exception_regno = 0;
49     enum eh_selector_regno = 1;
50 }
51 else version (ARM)
52 {
53     enum eh_exception_regno = 0;
54     enum eh_selector_regno = 1;
55 }
56 else version (PPC64)
57 {
58     enum eh_exception_regno = 3;
59     enum eh_selector_regno = 4;
60 }
61 else version (PPC)
62 {
63     enum eh_exception_regno = 3;
64     enum eh_selector_regno = 4;
65 }
66 else version (MIPS64)
67 {
68     enum eh_exception_regno = 4;
69     enum eh_selector_regno = 5;
70 }
71 else version (MIPS32)
72 {
73     enum eh_exception_regno = 4;
74     enum eh_selector_regno = 5;
75 }
76 else version (RISCV64)
77 {
78     enum eh_exception_regno = 10;
79     enum eh_selector_regno = 11;
80 }
81 else version (RISCV32)
82 {
83     enum eh_exception_regno = 10;
84     enum eh_selector_regno = 11;
85 }
86 else version (LoongArch64)
87 {
88     enum eh_exception_regno = 4;
89     enum eh_selector_regno = 5;
90 }
91 else
92 {
93     static assert(0, "Unknown EH register numbers for this architecture");
94 }
95 
96 extern (C)
97 {
98     int _d_isbaseof(ClassInfo b, ClassInfo c);
99     void _d_createTrace(Throwable o, void* context);
100 }
101 
102 private _Unwind_Ptr readUnaligned(T, bool consume)(ref const(ubyte)* p)
103 {
104     version (X86)         enum hasUnalignedLoads = true;
105     else version (X86_64) enum hasUnalignedLoads = true;
106     else                  enum hasUnalignedLoads = false;
107 
108     static if (hasUnalignedLoads)
109     {
110         T value = *cast(T*) p;
111     }
112     else
113     {
114         import core.stdc.string : memcpy;
115         T value = void;
116         memcpy(&value, p, T.sizeof);
117     }
118 
119     static if (consume)
120         p += T.sizeof;
121     return cast(_Unwind_Ptr) value;
122 }
123 
124 debug (EH_personality)
125 {
126     private void writeln(in char* format, ...) @nogc nothrow
127     {
128         import core.stdc.stdarg;
129 
130         va_list args;
131         va_start(args, format);
132         vfprintf(stdout, format, args);
133         fprintf(stdout, "\n");
134         fflush(stdout);
135     }
136 }
137 
138 /* High 4 bytes = vendor, low 4 bytes = language
139  * For us: "DMD\0D\0\0\0"
140  */
141 enum _Unwind_Exception_Class dmdExceptionClass =
142         (cast(_Unwind_Exception_Class)'D' << 56) |
143         (cast(_Unwind_Exception_Class)'M' << 48) |
144         (cast(_Unwind_Exception_Class)'D' << 40) |
145         (cast(_Unwind_Exception_Class)'D' << 24);
146 
147 /**
148  * Wrap the unwinder's data with our own compiler specific struct
149  * with our own data.
150  */
151 struct ExceptionHeader
152 {
153     Throwable object;                   // the thrown D object
154     _Unwind_Exception exception_object; // the unwinder's data
155 
156     // Save info on the handler that was detected
157     int handler;                        // which catch
158     const(ubyte)* languageSpecificData; // Language Specific Data Area for function enclosing the handler
159     _Unwind_Ptr landingPad;             // pointer to catch code
160 
161     // Stack other thrown exceptions in current thread through here.
162     ExceptionHeader* next;
163 
164     static ExceptionHeader* stack;      // thread local stack of chained exceptions
165 
166     /* Pre-allocate storage for 1 instance per thread.
167      * Use calloc/free for multiple exceptions in flight.
168      * Does not use GC
169      */
170     static ExceptionHeader ehstorage;
171 
172     /************
173      * Allocate and initialize an ExceptionHeader.
174      * Params:
175      *  o = thrown object
176      * Returns:
177      *  allocated and initalized ExceptionHeader
178      */
179     static ExceptionHeader* create(Throwable o) @nogc
180     {
181         auto eh = &ehstorage;
182         if (eh.object)                  // if in use
183         {
184             eh = cast(ExceptionHeader*)core.stdc.stdlib.calloc(ExceptionHeader.sizeof, 1);
185             if (!eh)
186                 terminate(__LINE__);              // out of memory while throwing - not much else can be done
187         }
188         eh.object = o;
189         eh.exception_object.exception_class = dmdExceptionClass;
190         debug (EH_personality) writeln("create(): %p", eh);
191         return eh;
192     }
193 
194     /**********************
195      * Free ExceptionHeader that was created by create().
196      * Params:
197      *  eh = ExceptionHeader to free
198      */
199     static void free(ExceptionHeader* eh)
200     {
201         debug (EH_personality) writeln("free(%p)", eh);
202         /* Smite contents even if subsequently free'd,
203          * to ward off dangling pointer bugs.
204          */
205         *eh = ExceptionHeader.init;
206         if (eh != &ehstorage)
207             core.stdc.stdlib.free(eh);
208     }
209 
210     /*************************
211      * Push this onto stack of chained exceptions.
212      */
213     void push()
214     {
215         next = stack;
216         stack = &this;
217     }
218 
219     /************************
220      * Pop and return top of chained exception stack.
221      */
222     static ExceptionHeader* pop()
223     {
224         auto eh = stack;
225         stack = eh.next;
226         return eh;
227     }
228 
229     /*******************************
230      * Convert from pointer to exception_object to pointer to ExceptionHeader
231      * that it is embedded inside of.
232      * Params:
233      *  eo = pointer to exception_object field
234      * Returns:
235      *  pointer to ExceptionHeader that eo points into.
236      */
237     static ExceptionHeader* toExceptionHeader(_Unwind_Exception* eo)
238     {
239         return cast(ExceptionHeader*)(cast(void*)eo - ExceptionHeader.exception_object.offsetof);
240     }
241 }
242 
243 /*******************************************
244  * The first thing a catch handler does is call this.
245  * Params:
246  *      exceptionObject = value passed to catch handler by unwinder
247  * Returns:
248  *      object that was caught
249  */
250 extern(C) Throwable __dmd_begin_catch(_Unwind_Exception* exceptionObject)
251 {
252     ExceptionHeader *eh = ExceptionHeader.toExceptionHeader(exceptionObject);
253     debug (EH_personality) writeln("__dmd_begin_catch(%p), object = %p", eh, eh.object);
254 
255     auto o = eh.object;
256     // Remove our reference to the exception. We should not decrease its refcount,
257     // because we pass the object on to the caller.
258     eh.object = null;
259 
260     // Pop off of chain
261     if (eh != ExceptionHeader.pop())
262         terminate(__LINE__);                      // eh should have been at top of stack
263 
264     _Unwind_DeleteException(&eh.exception_object);      // done with eh
265     return o;
266 }
267 
268 /****************************************
269  * Called when fibers switch contexts.
270  * Params:
271  *      newContext = stack to switch to
272  * Returns:
273  *      previous value of stack
274  */
275 extern(C) void* _d_eh_swapContextDwarf(void* newContext) nothrow @nogc
276 {
277     auto old = ExceptionHeader.stack;
278     ExceptionHeader.stack = cast(ExceptionHeader*)newContext;
279     return old;
280 }
281 
282 
283 /*********************
284  * Called by D code to throw an exception via
285  * ---
286  * throw o;
287  * ---
288  * Params:
289  *      o = Object to throw
290  * Returns:
291  *      doesn't return
292  */
293 extern(C) void _d_throwdwarf(Throwable o)
294 {
295     ExceptionHeader *eh = ExceptionHeader.create(o);
296 
297     eh.push();  // add to thrown exception stack
298     debug (EH_personality) writeln("_d_throwdwarf: eh = %p, eh.next = %p", eh, eh.next);
299 
300     /* Increment reference count if `o` is a refcounted Throwable
301      */
302     auto refcount = o.refcount();
303     if (refcount)       // non-zero means it's refcounted
304         o.refcount() = refcount + 1;
305 
306     /* Called by unwinder when exception object needs destruction by other than our code.
307      */
308     extern (C) static void exception_cleanup(_Unwind_Reason_Code reason, _Unwind_Exception* eo)
309     {
310         debug (EH_personality) writeln("exception_cleanup()");
311         switch (reason)
312         {
313             case _URC_FATAL_PHASE1_ERROR:       // unknown error code
314             case _URC_FATAL_PHASE2_ERROR:       // probably corruption
315             default:                            // uh-oh
316                 terminate(__LINE__);            // C++ calls terminate() instead
317                 break;
318 
319             case _URC_FOREIGN_EXCEPTION_CAUGHT:
320             case _URC_NO_REASON:
321                 auto eh = ExceptionHeader.toExceptionHeader(eo);
322                 ExceptionHeader.free(eh);
323                 break;
324         }
325 
326     }
327 
328     eh.exception_object.exception_cleanup = &exception_cleanup;
329 
330     _d_createTrace(o, null);
331 
332     auto r = _Unwind_RaiseException(&eh.exception_object);
333 
334     /* Shouldn't have returned, but if it did:
335      */
336     switch (r)
337     {
338         case _URC_END_OF_STACK:
339             /* Unwound the stack without encountering a catch clause.
340              * In C++, this would mean call uncaught_exception().
341              * In D, this can happen only if `rt_trapExceptions` is cleared
342              * since otherwise everything is enclosed by a top-level
343              * try/catch.
344              */
345             fprintf(stderr, "%s:%d: uncaught exception reached top of stack\n", __FILE__.ptr, __LINE__);
346             fprintf(stderr, "This might happen if you're missing a top level catch in your fiber or signal handler\n");
347             /**
348             As _d_print_throwable() itself may throw multiple times when calling core.demangle,
349             and with the uncaught exception still on the EH stack, this doesn't bode well with core.demangle's error recovery.
350             */
351             __dmd_begin_catch(&eh.exception_object);
352             _d_print_throwable(o);
353             abort();
354             assert(0);
355 
356         case _URC_FATAL_PHASE1_ERROR:
357             /* Unexpected error, likely some sort of corruption.
358              * In C++, terminate() would be called.
359              */
360             terminate(__LINE__);                          // should never happen
361             assert(0);
362 
363         case _URC_FATAL_PHASE2_ERROR:
364             /* Unexpected error. Program is in an unknown state.
365              * In C++, terminate() would be called.
366              */
367             terminate(__LINE__);                          // should never happen
368             assert(0);
369 
370         default:
371             terminate(__LINE__);                          // should never happen
372             assert(0);
373     }
374 }
375 
376 
377 /*****************************************
378  * "personality" function, specific to each language.
379  * This one, of course, is specific to DMD.
380  * Params:
381  *      ver = version must be 1
382  *      actions = bitwise OR of the 4 actions _UA_xxx.
383  *          _UA_SEARCH_PHASE means return _URC_HANDLER_FOUND if current frame has a handler,
384  *              _URC_CONTINUE_UNWIND if not. Cannot be used with _UA_CLEANUP_PHASE.
385  *          _UA_CLEANUP_PHASE means perform cleanup for current frame by calling nested functions
386  *              and returning _URC_CONTINUE_UNWIND. Or, set up registers and IP for Landing Pad
387  *              and return _URC_INSTALL_CONTEXT.
388  *          _UA_HANDLER_FRAME means this frame was the one with the handler in Phase 1, and now
389  *              it is Phase 2 and the handler must be run.
390  *          _UA_FORCE_UNWIND means unwinding the stack for longjmp or thread cancellation. Run
391  *              finally clauses, not catch clauses, finallys must end with call to _Uwind_Resume().
392  *      exceptionClass = 8 byte value indicating type of thrown exception. If the low 4 bytes
393  *          are "C++\0", it's a C++ exception.
394  *      exceptionObject = language specific exception information
395  *      context = opaque type of unwinder state information
396  * Returns:
397  *      reason code
398  * See_Also:
399  *      http://www.ucw.cz/~hubicka/papers/abi/node25.html
400  */
401 
402 extern (C) _Unwind_Reason_Code __dmd_personality_v0(int ver, _Unwind_Action actions,
403                _Unwind_Exception_Class exceptionClass, _Unwind_Exception* exceptionObject,
404                _Unwind_Context* context)
405 {
406     if (ver != 1)
407       return _URC_FATAL_PHASE1_ERROR;
408     assert(context);
409 
410     const(ubyte)* language_specific_data;
411     int handler;
412     _Unwind_Ptr landing_pad;
413 
414     debug (EH_personality)
415     {
416         writeln("__dmd_personality_v0(actions = x%x, eo = %p, context = %p)", cast(int)actions, exceptionObject, context);
417         writeln("exceptionClass = x%08llx", exceptionClass);
418 
419         if (exceptionClass == dmdExceptionClass)
420         {
421             auto eh = ExceptionHeader.toExceptionHeader(exceptionObject);
422             for (auto ehx = eh; ehx; ehx = ehx.next)
423                 writeln(" eh: %p next=%014p lsda=%p '%.*s'", ehx, ehx.next, ehx.languageSpecificData, ehx.object.msg.length, ehx.object.msg.ptr);
424         }
425     }
426 
427     language_specific_data = cast(const(ubyte)*)_Unwind_GetLanguageSpecificData(context);
428     debug (EH_personality) writeln("lsda = %p", language_specific_data);
429 
430     auto Start = _Unwind_GetRegionStart(context);
431 
432     /* Get instruction pointer (ip) at start of instruction that threw
433      */
434     version (CRuntime_Glibc)
435     {
436         int ip_before_insn;
437         // The instruction pointer must not be decremented when unwinding from a
438         // signal handler frame (asynchronous exception, also see
439         // etc.linux.memoryerror). So use _Unwind_GetIPInfo where available.
440         auto ip = _Unwind_GetIPInfo(context, &ip_before_insn);
441         if (!ip_before_insn)
442             --ip;
443     }
444     else
445     {
446         auto ip = _Unwind_GetIP(context);
447         --ip;
448     }
449     debug (EH_personality) writeln("ip = x%x", cast(int)(ip - Start));
450     debug (EH_personality) writeln("\tStart = %p, ipoff = %p, lsda = %p", Start, ip - Start, language_specific_data);
451 
452     auto result = scanLSDA(language_specific_data, ip - Start, exceptionClass,
453         (actions & _UA_FORCE_UNWIND) != 0,          // don't catch when forced unwinding
454         (actions & _UA_SEARCH_PHASE) != 0,          // search phase is looking for handlers
455         exceptionObject,
456         landing_pad,
457         handler);
458     landing_pad += Start;
459 
460     final switch (result)
461     {
462         case LsdaResult.notFound:
463             fprintf(stderr, "not found\n");
464             terminate(__LINE__);
465             assert(0);
466 
467         case LsdaResult.foreign:
468             terminate(__LINE__);
469             assert(0);
470 
471         case LsdaResult.corrupt:
472             fprintf(stderr, "LSDA is corrupt\n");
473             terminate(__LINE__);
474             assert(0);
475 
476         case LsdaResult.noAction:
477             debug (EH_personality) writeln("  no action");
478             return _URC_CONTINUE_UNWIND;
479 
480         case LsdaResult.cleanup:
481             debug (EH_personality) writeln("  cleanup");
482             if (actions & _UA_SEARCH_PHASE)
483             {
484                 return _URC_CONTINUE_UNWIND;
485             }
486             break;
487 
488         case LsdaResult.handler:
489             debug (EH_personality) writeln("  handler");
490             assert(!(actions & _UA_FORCE_UNWIND));
491             if (actions & _UA_SEARCH_PHASE)
492             {
493                 if (exceptionClass == dmdExceptionClass)
494                 {
495                     auto eh = ExceptionHeader.toExceptionHeader(exceptionObject);
496                     debug (EH_personality) writeln("   eh.lsda = %p, lsda = %p", eh.languageSpecificData, language_specific_data);
497                     eh.handler = handler;
498                     eh.languageSpecificData = language_specific_data;
499                     eh.landingPad = landing_pad;
500                 }
501                 return _URC_HANDLER_FOUND;
502             }
503             break;
504     }
505 
506     debug (EH_personality) writeln("  lsda = %p, landing_pad = %p, handler = %d", language_specific_data, landing_pad, handler);
507 
508     // Figure out what to do when there are multiple exceptions in flight
509     if (exceptionClass == dmdExceptionClass)
510     {
511         auto eh = ExceptionHeader.toExceptionHeader(exceptionObject);
512         debug (EH_personality) writeln(" '%.*s' next = %p", eh.object.msg.length, eh.object.msg.ptr, eh.next);
513         auto currentLsd = language_specific_data;
514         bool bypassed = false;
515         while (eh.next)
516         {
517             ExceptionHeader* ehn = eh.next;
518 
519             Error e = cast(Error)eh.object;
520             if (e !is null && !cast(Error)ehn.object)
521             {
522                 /* eh is an Error, ehn is not. Skip ehn.
523                  */
524                 debug (EH_personality) writeln("bypass");
525                 currentLsd = ehn.languageSpecificData;
526 
527                 // Continuing to construct the bypassed chain
528                 eh = ehn;
529                 bypassed = true;
530                 continue;
531             }
532 
533             // Don't combine when the exceptions are from different functions
534             if (currentLsd != ehn.languageSpecificData)
535             {
536                 debug (EH_personality) writeln("break: %p %p", currentLsd, ehn.languageSpecificData);
537                 break;
538             }
539 
540             else
541             {
542                 debug (EH_personality) writeln("chain");
543                 // Append eh's object to ehn's object chain
544                 // And replace our exception object with in-flight one
545                 eh.object = Throwable.chainTogether(ehn.object, eh.object);
546 
547                 if (ehn.handler != handler && !bypassed)
548                 {
549                     handler = ehn.handler;
550 
551                     eh.handler = handler;
552                     eh.languageSpecificData = language_specific_data;
553                     eh.landingPad = landing_pad;
554                 }
555             }
556 
557             // Remove ehn from threaded chain
558             eh.next = ehn.next;
559             debug (EH_personality) writeln("delete %p", ehn);
560             _Unwind_DeleteException(&ehn.exception_object); // discard ehn
561         }
562         if (bypassed)
563         {
564             eh = ExceptionHeader.toExceptionHeader(exceptionObject);
565             Error e = cast(Error)eh.object;
566             auto ehn = eh.next;
567             e.bypassedException = ehn.object;
568             eh.next = ehn.next;
569             _Unwind_DeleteException(&ehn.exception_object);
570         }
571     }
572 
573     // Set up registers and jump to cleanup or handler
574     _Unwind_SetGR(context, eh_exception_regno, cast(_Unwind_Ptr)exceptionObject);
575     _Unwind_SetGR(context, eh_selector_regno, handler);
576     _Unwind_SetIP(context, landing_pad);
577 
578     return _URC_INSTALL_CONTEXT;
579 }
580 
581 /*************************************************
582  * Look at the chain of inflight exceptions and pick the class type that'll
583  * be looked for in catch clauses.
584  * Params:
585  *      exceptionObject = language specific exception information
586  *      currentLsd = pointer to LSDA table
587  * Returns:
588  *      class type to look for
589  */
590 ClassInfo getClassInfo(_Unwind_Exception* exceptionObject, const(ubyte)* currentLsd)
591 {
592     ExceptionHeader* eh = ExceptionHeader.toExceptionHeader(exceptionObject);
593     Throwable ehobject = eh.object;
594     debug (EH_personality) writeln("start: %p '%.*s'", ehobject, cast(int)(typeid(ehobject).info.name.length), ehobject.classinfo.info.name.ptr);
595     for (ExceptionHeader* ehn = eh.next; ehn; ehn = ehn.next)
596     {
597         // like __dmd_personality_v0, don't combine when the exceptions are from different functions
598         // Fixes "exception thrown and caught while inside finally block"
599         // https://issues.dlang.org/show_bug.cgi?id=19831
600         if (currentLsd != ehn.languageSpecificData)
601         {
602             debug (EH_personality) writeln("break: %p %p", currentLsd, ehn.languageSpecificData);
603             break;
604         }
605 
606         debug (EH_personality) writeln("ehn =   %p '%.*s'", ehn.object, cast(int)(typeid(ehn.object).info.name.length), ehn.object.classinfo.info.name.ptr);
607         Error e = cast(Error)ehobject;
608         if (e is null || (cast(Error)ehn.object) !is null)
609         {
610             ehobject = ehn.object;
611         }
612     }
613     debug (EH_personality) writeln("end  : %p", ehobject);
614     return typeid(ehobject);
615 }
616 
617 /******************************
618  * Decode Unsigned LEB128.
619  * Params:
620  *      p = pointer to data pointer, *p is updated
621  *      to point past decoded value
622  * Returns:
623  *      decoded value
624  * See_Also:
625  *      https://en.wikipedia.org/wiki/LEB128
626  */
627 _uleb128_t uLEB128(const(ubyte)** p)
628 {
629     auto q = *p;
630     _uleb128_t result = 0;
631     uint shift = 0;
632     while (1)
633     {
634         ubyte b = *q++;
635         result |= cast(_uleb128_t)(b & 0x7F) << shift;
636         if ((b & 0x80) == 0)
637             break;
638         shift += 7;
639     }
640     *p = q;
641     return result;
642 }
643 
644 /******************************
645  * Decode Signed LEB128.
646  * Params:
647  *      p = pointer to data pointer, *p is updated
648  *      to point past decoded value
649  * Returns:
650  *      decoded value
651  * See_Also:
652  *      https://en.wikipedia.org/wiki/LEB128
653  */
654 _sleb128_t sLEB128(const(ubyte)** p)
655 {
656     auto q = *p;
657     ubyte b;
658 
659     _sleb128_t result = 0;
660     uint shift = 0;
661     while (1)
662     {
663         b = *q++;
664         result |= cast(_sleb128_t)(b & 0x7F) << shift;
665         shift += 7;
666         if ((b & 0x80) == 0)
667             break;
668     }
669     if (shift < result.sizeof * 8 && (b & 0x40))
670         result |= -(cast(_sleb128_t)1 << shift);
671     *p = q;
672     return result;
673 }
674 
675 enum
676 {
677         DW_EH_PE_FORMAT_MASK    = 0x0F,
678         DW_EH_PE_APPL_MASK      = 0x70,
679         DW_EH_PE_indirect       = 0x80,
680 
681         DW_EH_PE_omit           = 0xFF,
682         DW_EH_PE_ptr            = 0x00,
683         DW_EH_PE_uleb128        = 0x01,
684         DW_EH_PE_udata2         = 0x02,
685         DW_EH_PE_udata4         = 0x03,
686         DW_EH_PE_udata8         = 0x04,
687         DW_EH_PE_sleb128        = 0x09,
688         DW_EH_PE_sdata2         = 0x0A,
689         DW_EH_PE_sdata4         = 0x0B,
690         DW_EH_PE_sdata8         = 0x0C,
691 
692         DW_EH_PE_absptr         = 0x00,
693         DW_EH_PE_pcrel          = 0x10,
694         DW_EH_PE_textrel        = 0x20,
695         DW_EH_PE_datarel        = 0x30,
696         DW_EH_PE_funcrel        = 0x40,
697         DW_EH_PE_aligned        = 0x50,
698 }
699 
700 
701 /**************************************************
702  * Read and extract information from the LSDA (aka gcc_except_table section).
703  * The dmd Call Site Table is structurally different from other implementations. It
704  * is organized as nested ranges, and one ip can map to multiple ranges. The most
705  * nested candidate is selected when searched. Other implementations have one candidate
706  * per ip.
707  * Params:
708  *      lsda = pointer to LSDA table
709  *      ip = offset from start of function at which exception happened
710  *      exceptionClass = which language threw the exception
711  *      cleanupsOnly = only look for cleanups
712  *      preferHandler = if a handler encloses a cleanup, prefer the handler
713  *      exceptionObject = language specific exception information
714  *      landingPad = set to landing pad
715  *      handler = set to index of which catch clause was matched
716  * Returns:
717  *      LsdaResult
718  * See_Also:
719  *      http://reverseengineering.stackexchange.com/questions/6311/how-to-recover-the-exception-info-from-gcc-except-table-and-eh-handle-sections
720  *      http://www.airs.com/blog/archives/464
721  *      https://anarcheuz.github.io/2015/02/15/ELF%20internals%20part%202%20-%20exception%20handling/
722  */
723 
724 LsdaResult scanLSDA(const(ubyte)* lsda, _Unwind_Ptr ip, _Unwind_Exception_Class exceptionClass,
725         bool cleanupsOnly,
726         bool preferHandler,
727         _Unwind_Exception* exceptionObject,
728         out _Unwind_Ptr landingPad, out int handler)
729 {
730     auto p = lsda;
731     if (!p)
732         return LsdaResult.noAction;
733 
734     _Unwind_Ptr dw_pe_value(ubyte pe)
735     {
736         switch (pe)
737         {
738             case DW_EH_PE_sdata2:   return readUnaligned!(short,  true)(p);
739             case DW_EH_PE_udata2:   return readUnaligned!(ushort, true)(p);
740             case DW_EH_PE_sdata4:   return readUnaligned!(int,    true)(p);
741             case DW_EH_PE_udata4:   return readUnaligned!(uint,   true)(p);
742             case DW_EH_PE_sdata8:   return readUnaligned!(long,   true)(p);
743             case DW_EH_PE_udata8:   return readUnaligned!(ulong,  true)(p);
744             case DW_EH_PE_sleb128:  return cast(_Unwind_Ptr) sLEB128(&p);
745             case DW_EH_PE_uleb128:  return cast(_Unwind_Ptr) uLEB128(&p);
746             case DW_EH_PE_ptr:      if (size_t.sizeof == 8)
747                                         goto case DW_EH_PE_udata8;
748                                     else
749                                         goto case DW_EH_PE_udata4;
750             default:
751                 terminate(__LINE__);
752                 return 0;
753         }
754     }
755 
756     ubyte LPstart = *p++;
757 
758     _Unwind_Ptr LPbase = 0;
759     if (LPstart != DW_EH_PE_omit)
760     {
761         LPbase = dw_pe_value(LPstart);
762     }
763 
764     ubyte TType = *p++;
765     _Unwind_Ptr TTbase = 0;
766     _Unwind_Ptr TToffset = 0;
767     if (TType != DW_EH_PE_omit)
768     {
769         TTbase = uLEB128(&p);
770         TToffset = (p - lsda) + TTbase;
771     }
772     debug (EH_personality) writeln("  TType = x%x, TTbase = x%x", TType, cast(int)TTbase);
773 
774     ubyte CallSiteFormat = *p++;
775 
776     _Unwind_Ptr CallSiteTableSize = dw_pe_value(DW_EH_PE_uleb128);
777     debug (EH_personality) writeln("  CallSiteFormat = x%x, CallSiteTableSize = x%x", CallSiteFormat, cast(int)CallSiteTableSize);
778 
779     _Unwind_Ptr ipoffset = ip - LPbase;
780     debug (EH_personality) writeln("ipoffset = x%x", cast(int)ipoffset);
781     bool noAction = false;
782     auto tt = lsda + TToffset;
783     const(ubyte)* pActionTable = p + CallSiteTableSize;
784     while (1)
785     {
786         if (p >= pActionTable)
787         {
788             if (p == pActionTable)
789                 break;
790             fprintf(stderr, "no Call Site Table\n");
791 
792             return LsdaResult.corrupt;
793         }
794 
795         _Unwind_Ptr CallSiteStart = dw_pe_value(CallSiteFormat);
796         _Unwind_Ptr CallSiteRange = dw_pe_value(CallSiteFormat);
797         _Unwind_Ptr LandingPad    = dw_pe_value(CallSiteFormat);
798         _uleb128_t ActionRecordPtr = uLEB128(&p);
799 
800         debug (EH_personality)
801         {
802             writeln(" XT: start = x%x, range = x%x, landing pad = x%x, action = x%x",
803                 cast(int)CallSiteStart, cast(int)CallSiteRange, cast(int)LandingPad, cast(int)ActionRecordPtr);
804         }
805 
806         if (ipoffset < CallSiteStart)
807             break;
808 
809         // The most nested entry will be the last one that ip is in
810         if (ipoffset < CallSiteStart + CallSiteRange)
811         {
812             debug (EH_personality) writeln("\tmatch");
813             if (ActionRecordPtr)                // if saw a catch
814             {
815                 if (cleanupsOnly)
816                     continue;                   // ignore catch
817 
818                 auto h = actionTableLookup(exceptionObject, cast(uint)ActionRecordPtr, pActionTable, tt, TType, exceptionClass, lsda);
819                 if (h < 0)
820                 {
821                     fprintf(stderr, "negative handler\n");
822                     return LsdaResult.corrupt;
823                 }
824                 if (h == 0)
825                     continue;                   // ignore
826 
827                 // The catch is good
828                 noAction = false;
829                 landingPad = LandingPad;
830                 handler = h;
831             }
832             else if (LandingPad)                // if saw a cleanup
833             {
834                 if (preferHandler && handler)   // enclosing handler overrides cleanup
835                     continue;                   // keep looking
836                 noAction = false;
837                 landingPad = LandingPad;
838                 handler = 0;                    // cleanup hides the handler
839             }
840             else                                // take no action
841                 noAction = true;
842         }
843     }
844 
845     if (noAction)
846     {
847         assert(!landingPad && !handler);
848         return LsdaResult.noAction;
849     }
850 
851     if (landingPad)
852         return handler ? LsdaResult.handler : LsdaResult.cleanup;
853 
854     return LsdaResult.notFound;
855 }
856 
857 /********************************************
858  * Look up classType in Action Table.
859  * Params:
860  *      exceptionObject = language specific exception information
861  *      actionRecordPtr = starting index in Action Table + 1
862  *      pActionTable = pointer to start of Action Table
863  *      tt = pointer past end of Type Table
864  *      TType = encoding of entries in Type Table
865  *      exceptionClass = which language threw the exception
866  *      lsda = pointer to LSDA table
867  * Returns:
868  *      - &gt;=1 means the handler index of the classType
869  *      - 0 means classType is not in the Action Table
870  *      - &lt;0 means corrupt
871  */
872 int actionTableLookup(_Unwind_Exception* exceptionObject, uint actionRecordPtr, const(ubyte)* pActionTable,
873                       const(ubyte)* tt, ubyte TType, _Unwind_Exception_Class exceptionClass, const(ubyte)* lsda)
874 {
875     debug (EH_personality)
876     {
877         writeln("actionTableLookup(actionRecordPtr = %d, pActionTable = %p, tt = %p)",
878             actionRecordPtr, pActionTable, tt);
879     }
880     assert(pActionTable < tt);
881 
882     ClassInfo thrownType;
883     if (exceptionClass == dmdExceptionClass)
884     {
885         thrownType = getClassInfo(exceptionObject, lsda);
886     }
887 
888     for (auto ap = pActionTable + actionRecordPtr - 1; 1; )
889     {
890         assert(pActionTable <= ap && ap < tt);
891 
892         auto TypeFilter = sLEB128(&ap);
893         auto apn = ap;
894         auto NextRecordPtr = sLEB128(&ap);
895 
896         debug (EH_personality) writeln(" at: TypeFilter = %d, NextRecordPtr = %d", cast(int)TypeFilter, cast(int)NextRecordPtr);
897 
898         if (TypeFilter <= 0)                    // should never happen with DMD generated tables
899         {
900             fprintf(stderr, "TypeFilter = %d\n", cast(int)TypeFilter);
901             return -1;                          // corrupt
902         }
903 
904         /* TypeFilter is negative index from TToffset,
905          * which is where the ClassInfo is stored
906          */
907         _Unwind_Ptr entry;
908         const(ubyte)* tt2;
909         switch (TType & DW_EH_PE_FORMAT_MASK)
910         {
911             case DW_EH_PE_sdata2:   entry = readUnaligned!(short,  false)(tt2 = tt - TypeFilter * 2); break;
912             case DW_EH_PE_udata2:   entry = readUnaligned!(ushort, false)(tt2 = tt - TypeFilter * 2); break;
913             case DW_EH_PE_sdata4:   entry = readUnaligned!(int,    false)(tt2 = tt - TypeFilter * 4); break;
914             case DW_EH_PE_udata4:   entry = readUnaligned!(uint,   false)(tt2 = tt - TypeFilter * 4); break;
915             case DW_EH_PE_sdata8:   entry = readUnaligned!(long,   false)(tt2 = tt - TypeFilter * 8); break;
916             case DW_EH_PE_udata8:   entry = readUnaligned!(ulong,  false)(tt2 = tt - TypeFilter * 8); break;
917             case DW_EH_PE_ptr:      if (size_t.sizeof == 8)
918                                         goto case DW_EH_PE_udata8;
919                                     else
920                                         goto case DW_EH_PE_udata4;
921             default:
922                 fprintf(stderr, "TType = x%x\n", TType);
923                 return -1;      // corrupt
924         }
925         if (!entry)             // the 'catch all' type
926             return -1;          // corrupt: should never happen with DMD, which explicitly uses Throwable
927 
928         switch (TType & DW_EH_PE_APPL_MASK)
929         {
930             case DW_EH_PE_absptr:
931                 break;
932 
933             case DW_EH_PE_pcrel:
934                 entry += cast(_Unwind_Ptr)tt2;
935                 break;
936 
937             default:
938                 return -1;
939         }
940         if (TType & DW_EH_PE_indirect)
941             entry = *cast(_Unwind_Ptr*)entry;
942 
943         ClassInfo ci = cast(ClassInfo)cast(void*)(entry);
944         if (typeid(ci) is typeid(__cpp_type_info_ptr))
945         {
946             if (exceptionClass == cppExceptionClass || exceptionClass == cppExceptionClass1)
947             {
948                 // sti is catch clause type_info
949                 auto sti = cast(CppTypeInfo)((cast(__cpp_type_info_ptr)cast(void*)ci).ptr);
950                 auto p = getCppPtrToThrownObject(exceptionObject, sti);
951                 if (p) // if found
952                 {
953                     auto eh = CppExceptionHeader.toExceptionHeader(exceptionObject);
954                     eh.thrownPtr = p;                   // for __cxa_begin_catch()
955                     return cast(int)TypeFilter;
956                 }
957             }
958         }
959         else if (exceptionClass == dmdExceptionClass && _d_isbaseof(thrownType, ci))
960             return cast(int)TypeFilter; // found it
961 
962         if (!NextRecordPtr)
963             return 0;                   // catch not found
964 
965         ap = apn + NextRecordPtr;
966     }
967     assert(false); // All other branches return
968 }
969 
970 enum LsdaResult
971 {
972     notFound,   // ip was not found in the LSDA - an exception shouldn't have happened
973     foreign,    // found a result we cannot handle
974     corrupt,    // the tables are corrupt
975     noAction,   // found, but no action needed (i.e. no cleanup nor handler)
976     cleanup,    // cleanup found (i.e. finally or destructor)
977     handler,    // handler found (i.e. a catch)
978 }
979 
980 void terminate(uint line) @nogc
981 {
982     printf("dwarfeh(%u) fatal error\n", line);
983     abort();     // unceremoniously exit
984 }
985 
986 
987 /****************************** C++ Support *****************************/
988 
989 enum _Unwind_Exception_Class cppExceptionClass =
990         (cast(_Unwind_Exception_Class)'G' << 56) |
991         (cast(_Unwind_Exception_Class)'N' << 48) |
992         (cast(_Unwind_Exception_Class)'U' << 40) |
993         (cast(_Unwind_Exception_Class)'C' << 32) |
994         (cast(_Unwind_Exception_Class)'C' << 24) |
995         (cast(_Unwind_Exception_Class)'+' << 16) |
996         (cast(_Unwind_Exception_Class)'+' <<  8) |
997         (cast(_Unwind_Exception_Class)0 <<  0);
998 
999 enum _Unwind_Exception_Class cppExceptionClass1 = cppExceptionClass + 1;
1000 
1001 
1002 /*****************************************
1003  * Get Pointer to Thrown Object if type of thrown object is implicitly
1004  * convertible to the catch type.
1005  * Params:
1006  *      exceptionObject = language specific exception information
1007  *      sti = type of catch clause
1008  * Returns:
1009  *      null if not caught, pointer to thrown object if caught
1010  */
1011 void* getCppPtrToThrownObject(_Unwind_Exception* exceptionObject, CppTypeInfo sti)
1012 {
1013     void* p;    // pointer to thrown object
1014     if (exceptionObject.exception_class & 1)
1015         p = CppExceptionHeader.toExceptionHeader(exceptionObject).ptr;
1016     else
1017         p = cast(void*)(exceptionObject + 1);           // thrown object is immediately after it
1018 
1019     const tt = (cast(CppExceptionHeader*)p - 1).typeinfo;
1020 
1021     if (tt.__is_pointer_p())
1022         p = *cast(void**)p;
1023 
1024     // Pointer adjustment may be necessary due to multiple inheritance
1025     return (sti is tt || sti.__do_catch(tt, &p, 1)) ? p : null;
1026 }
1027 
1028 extern (C++)
1029 {
1030     /**
1031      * Access C++ std::type_info's virtual functions from D,
1032      * being careful to not require linking with libstd++
1033      * or interfere with core.stdcpp.typeinfo.
1034      * So, give it a different name.
1035      */
1036     interface CppTypeInfo // map to C++ std::type_info's virtual functions
1037     {
1038         void dtor1();                           // consume destructor slot in vtbl[]
1039         void dtor2();                           // consume destructor slot in vtbl[]
1040         bool __is_pointer_p() const;
1041         bool __is_function_p() const;
1042         bool __do_catch(const CppTypeInfo, void**, uint) const;
1043         bool __do_upcast(const void*, void**) const;
1044     }
1045 }
1046 
1047 /// The C++ version of D's ExceptionHeader wrapper
1048 struct CppExceptionHeader
1049 {
1050     union
1051     {
1052         CppTypeInfo typeinfo;                   // type that was thrown
1053         void* ptr;                              // pointer to real exception
1054     }
1055     void* p1;                                   // unreferenced placeholders...
1056     void* p2;
1057     void* p3;
1058     void* p4;
1059     int i1;
1060     int i2;
1061     const(ubyte)* p5;
1062     const(ubyte)* p6;
1063     _Unwind_Ptr p7;
1064     void* thrownPtr;                            // pointer to thrown object
1065     _Unwind_Exception exception_object;         // the unwinder's data
1066 
1067     /*******************************
1068      * Convert from pointer to exception_object field to pointer to CppExceptionHeader
1069      * that it is embedded inside of.
1070      * Params:
1071      *  eo = pointer to exception_object field
1072      * Returns:
1073      *  pointer to CppExceptionHeader that eo points into.
1074      */
1075     static CppExceptionHeader* toExceptionHeader(_Unwind_Exception* eo)
1076     {
1077         return cast(CppExceptionHeader*)(eo + 1) - 1;
1078     }
1079 }