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