1 module rt.eh.ldc_dwarf;
2 
3 import unwind;
4 import lwdr.tracking;
5 import lifetime.class_ : _d_isbaseof;
6 
7 enum GNU_ARM_EABI_Unwinder = true;
8 
9 /**
10 * Declare all known and handled exception classes.
11 * D exceptions -- "GNUCD\0\0\0".
12 * C++ exceptions -- "GNUCC++\0"
13 * C++ dependent exceptions -- "GNUCC++\x01"
14 */
15 static if (GNU_ARM_EABI_Unwinder)
16 {
17     enum _Unwind_Exception_Class gdcExceptionClass = "GNUCD\0\0\0";
18     enum _Unwind_Exception_Class gxxExceptionClass = "GNUCC++\0";
19     enum _Unwind_Exception_Class gxxDependentExceptionClass = "GNUCC++\x01";
20 }
21 else
22 {
23     enum _Unwind_Exception_Class gdcExceptionClass =
24         (cast(_Unwind_Exception_Class)'G' << 56) |
25         (cast(_Unwind_Exception_Class)'N' << 48) |
26         (cast(_Unwind_Exception_Class)'U' << 40) |
27         (cast(_Unwind_Exception_Class)'C' << 32) |
28         (cast(_Unwind_Exception_Class)'D' << 24);
29 
30     enum _Unwind_Exception_Class gxxExceptionClass =
31         (cast(_Unwind_Exception_Class)'G' << 56) |
32         (cast(_Unwind_Exception_Class)'N' << 48) |
33         (cast(_Unwind_Exception_Class)'U' << 40) |
34         (cast(_Unwind_Exception_Class)'C' << 32) |
35         (cast(_Unwind_Exception_Class)'C' << 24) |
36         (cast(_Unwind_Exception_Class)'+' << 16) |
37         (cast(_Unwind_Exception_Class)'+' <<  8) |
38         (cast(_Unwind_Exception_Class)0 <<  0);
39 
40     enum _Unwind_Exception_Class gxxDependentExceptionClass =
41         gxxExceptionClass + 1;
42 }
43 
44 /**
45 * Checks for GDC exception class.
46 */
47 bool isGdcExceptionClass(_Unwind_Exception_Class c) @nogc
48 {
49     static if (GNU_ARM_EABI_Unwinder)
50     {
51         return c[0] == gdcExceptionClass[0]
52             && c[1] == gdcExceptionClass[1]
53             && c[2] == gdcExceptionClass[2]
54             && c[3] == gdcExceptionClass[3]
55             && c[4] == gdcExceptionClass[4]
56             && c[5] == gdcExceptionClass[5]
57             && c[6] == gdcExceptionClass[6]
58             && c[7] == gdcExceptionClass[7];
59     }
60     else
61     {
62         return c == gdcExceptionClass;
63     }
64 }
65 
66 /**
67 * Checks for any C++ exception class.
68 */
69 bool isGxxExceptionClass(_Unwind_Exception_Class c) @nogc
70 {
71     static if (GNU_ARM_EABI_Unwinder)
72     {
73         return c[0] == gxxExceptionClass[0]
74             && c[1] == gxxExceptionClass[1]
75             && c[2] == gxxExceptionClass[2]
76             && c[3] == gxxExceptionClass[3]
77             && c[4] == gxxExceptionClass[4]
78             && c[5] == gxxExceptionClass[5]
79             && c[6] == gxxExceptionClass[6]
80             && (c[7] == gxxExceptionClass[7]
81                 || c[7] == gxxDependentExceptionClass[7]);
82     }
83     else
84     {
85         return c == gxxExceptionClass
86             || c == gxxDependentExceptionClass;
87     }
88 }
89 
90 /**
91 * Checks for primary or dependent, but not that it is a C++ exception.
92 */
93 bool isDependentException(_Unwind_Exception_Class c) @nogc
94 {
95     static if (GNU_ARM_EABI_Unwinder)
96         return (c[7] == '\x01');
97     else
98         return (c & 1);
99 }
100 
101 /**
102 * A D exception object consists of a header, which is a wrapper
103 * around an unwind object header with additional D specific
104 * information, prefixed by the exception object itself.
105 */
106 struct ExceptionHeader
107 {
108     // Because of a lack of __aligned__ style attribute, our object
109     // and the unwind object are the first two fields.
110     static if (Throwable.alignof < _Unwind_Exception.alignof)
111         ubyte[_Unwind_Exception.alignof - Throwable.alignof] pad;
112 
113     // The object being thrown.  The compiled code expects this to
114     // be immediately before the generic exception header.
115     Throwable object;
116 
117     // The generic exception header.
118     _Unwind_Exception unwindHeader;
119 
120     static assert(unwindHeader.offsetof - object.offsetof == object.sizeof);
121 
122     // Cache handler details between Phase 1 and Phase 2.
123     static if (GNU_ARM_EABI_Unwinder)
124     {
125         // Nothing here yet.
126     }
127     else
128     {
129         // Which catch was found.
130         int handler;
131 
132         // Language Specific Data Area for function enclosing the handler.
133         const(ubyte)* languageSpecificData;
134 
135         // Pointer to catch code.
136         _Unwind_Ptr landingPad;
137 
138         // Canonical Frame Address (CFA) for the enclosing handler.
139         _Unwind_Word canonicalFrameAddress;
140     }
141 
142     // Stack other thrown exceptions in current thread through here.
143     ExceptionHeader* next;
144 
145     // Thread local stack of chained exceptions.
146     static ExceptionHeader* stack;
147 
148     // Pre-allocate storage for 1 instance per thread.
149     // Use calloc/free for multiple exceptions in flight.
150     static ExceptionHeader ehstorage;
151 
152     /**
153 	* Allocate and initialize an ExceptionHeader.
154 	*/
155     static ExceptionHeader* create(Throwable o) @nogc
156     {
157         auto eh = &ehstorage;
158 
159         // Check exception object in use.
160         if (eh.object)
161         {
162             eh = cast(ExceptionHeader*) lwdrInternal_alloc(ExceptionHeader.sizeof);
163             // Out of memory while throwing - not much else can be done.
164             if (!eh)
165                    assert(false);
166                 //terminate("out of memory", __LINE__);
167         }
168         eh.object = o;
169 
170         eh.unwindHeader.exception_class = gdcExceptionClass;
171 
172         return eh;
173     }
174 
175     /**
176 	* Free ExceptionHeader that was created by create().
177 	*/
178     static void free(ExceptionHeader* eh) @nogc
179     {
180         *eh = ExceptionHeader.init;
181         if (eh != &ehstorage)
182             lwdrInternal_free(eh);
183     }
184 
185     /**
186 	* Push this onto stack of chained exceptions.
187 	*/
188     void push() @nogc
189     {
190         next = stack;
191         stack = &this;
192     }
193 
194     /**
195 	* Pop and return top of chained exception stack.
196 	*/
197     static ExceptionHeader* pop() @nogc
198     {
199         auto eh = stack;
200         stack = eh.next;
201         return eh;
202     }
203 
204     /**
205 	* Save stage1 handler information in the exception object.
206 	*/
207     static void save(_Unwind_Exception* unwindHeader,
208                      _Unwind_Word cfa, int handler,
209                      const(ubyte)* lsda, _Unwind_Ptr landingPad) @nogc
210 					 {
211 						static if (GNU_ARM_EABI_Unwinder)
212 						{
213 							unwindHeader.barrier_cache.sp = cfa;
214 							unwindHeader.barrier_cache.bitpattern[1] = cast(_uw)handler;
215 							unwindHeader.barrier_cache.bitpattern[2] = cast(_uw)lsda;
216 							unwindHeader.barrier_cache.bitpattern[3] = cast(_uw)landingPad;
217 						}
218 						else
219 						{
220 							ExceptionHeader* eh = toExceptionHeader(unwindHeader);
221 							eh.canonicalFrameAddress = cfa;
222 							eh.handler = handler;
223 							eh.languageSpecificData = lsda;
224 							eh.landingPad = landingPad;
225 						}
226 					 }
227 
228     /**
229 	* Restore the catch handler data saved during phase1.
230 	*/
231     static void restore(_Unwind_Exception* unwindHeader, out int handler,
232                         out const(ubyte)* lsda, out _Unwind_Ptr landingPad,
233                         out _Unwind_Word cfa) @nogc
234 						{
235 							static if (GNU_ARM_EABI_Unwinder)
236 							{
237 								cfa = unwindHeader.barrier_cache.sp;
238 								handler = cast(int)unwindHeader.barrier_cache.bitpattern[1];
239 								lsda = cast(ubyte*)unwindHeader.barrier_cache.bitpattern[2];
240 								landingPad = cast(_Unwind_Ptr)unwindHeader.barrier_cache.bitpattern[3];
241 							}
242 							else
243 							{
244 								ExceptionHeader* eh = toExceptionHeader(unwindHeader);
245 								cfa = eh.canonicalFrameAddress;
246 								handler = eh.handler;
247 								lsda = eh.languageSpecificData;
248 								landingPad = cast(_Unwind_Ptr)eh.landingPad;
249 							}
250 						}
251 
252     /**
253 	* Convert from pointer to unwindHeader to pointer to ExceptionHeader
254 	* that it is embedded inside of.
255 	*/
256     static ExceptionHeader* toExceptionHeader(_Unwind_Exception* exc) @nogc
257     {
258         return cast(ExceptionHeader*)(cast(void*)exc - ExceptionHeader.unwindHeader.offsetof);
259     }
260 }
261 
262 /**
263 * Map to C++ std::type_info's virtual functions from D,
264 * being careful to not require linking with libstdc++.
265 * So it is given a different name.
266 */
267 extern(C++) interface CxxTypeInfo
268 {
269     void dtor1();
270     void dtor2();
271     bool __is_pointer_p() const;
272     bool __is_function_p() const;
273     bool __do_catch(const CxxTypeInfo, void**, uint) const;
274     bool __do_upcast(const void*, void**) const;
275 }
276 
277 /**
278 * Structure of a C++ exception, represented as a C structure.
279 * See unwind-cxx.h for the full definition.
280 */
281 struct CxaExceptionHeader
282 {
283     union
284     {
285         CxxTypeInfo exceptionType;
286         void* primaryException;
287     }
288     void function(void*) exceptionDestructor;
289     void function() unexpectedHandler;
290     void function() terminateHandler;
291     CxaExceptionHeader* nextException;
292     int handlerCount;
293 
294     static if (GNU_ARM_EABI_Unwinder)
295     {
296         CxaExceptionHeader* nextPropagatingException;
297         int propagationCount;
298     }
299     else
300     {
301         int handlerSwitchValue;
302         const(ubyte)* actionRecord;
303         const(ubyte)* languageSpecificData;
304         _Unwind_Ptr catchTemp;
305         void* adjustedPtr;
306     }
307 
308     _Unwind_Exception unwindHeader;
309 
310     /**
311 	* There's no saving between phases, so only cache pointer.
312 	* __cxa_begin_catch expects this to be set.
313 	*/
314     static void save(_Unwind_Exception* unwindHeader, void* thrownPtr) @nogc
315     {
316         static if (GNU_ARM_EABI_Unwinder)
317             unwindHeader.barrier_cache.bitpattern[0] = cast(_uw) thrownPtr;
318         else
319         {
320             auto eh = toExceptionHeader(unwindHeader);
321             eh.adjustedPtr = thrownPtr;
322         }
323     }
324 
325     /**
326 	* Get pointer to the thrown object if the thrown object type behind the
327 	* exception is implicitly convertible to the catch type.
328 	*/
329     static void* getAdjustedPtr(_Unwind_Exception* exc, CxxTypeInfo catchType)
330     {
331         void* thrownPtr;
332 
333         // A dependent C++ exceptions is just a wrapper around the unwind header.
334         // A primary C++ exception has the thrown object located immediately after it.
335         if (isDependentException(exc.exception_class))
336             thrownPtr = toExceptionHeader(exc).primaryException;
337         else
338             thrownPtr = cast(void*)(exc + 1);
339 
340         // Pointer types need to adjust the actual pointer, not the pointer that is
341         // the exception object.  This also has the effect of passing pointer types
342         // "by value" through the __cxa_begin_catch return value.
343         const throw_type = (cast(CxaExceptionHeader*)thrownPtr - 1).exceptionType;
344 
345         if (throw_type.__is_pointer_p())
346             thrownPtr = *cast(void**)thrownPtr;
347 
348         // Pointer adjustment may be necessary due to multiple inheritance
349         if (catchType is throw_type
350             || catchType.__do_catch(throw_type, &thrownPtr, 1))
351             return thrownPtr;
352 
353         return null;
354     }
355 
356     /**
357 	* Convert from pointer to unwindHeader to pointer to CxaExceptionHeader
358 	* that it is embedded inside of.
359 	*/
360     static CxaExceptionHeader* toExceptionHeader(_Unwind_Exception* exc) @nogc
361     {
362         return cast(CxaExceptionHeader*)(exc + 1) - 1;
363     }
364 }
365 
366 /**
367 * Called if exception handling must be abandoned for any reason.
368 */
369 private void terminate(string msg, uint line) @nogc
370 {
371     assert(false);
372 }
373 
374 /**
375 * Called when fibers switch contexts.
376 */
377 extern(C) void* _d_eh_swapContext(void* newContext) nothrow @nogc
378 {
379     auto old = ExceptionHeader.stack;
380     ExceptionHeader.stack = cast(ExceptionHeader*)newContext;
381     return old;
382 }
383 
384 extern(C) void* _d_eh_enter_catch(_Unwind_Exception* unwindHeader)
385 {
386     return __gdc_begin_catch(unwindHeader);
387 }
388 
389 /**
390 * Called before starting a catch.  Returns the exception object.
391 */
392 extern(C) void* __gdc_begin_catch(_Unwind_Exception* unwindHeader)
393 {
394     ExceptionHeader* header = ExceptionHeader.toExceptionHeader(unwindHeader);
395 
396     void* objectp = cast(void*)header.object;
397 
398     // Something went wrong when stacking up chained headers...
399     if (header != ExceptionHeader.pop())
400         terminate("catch error", __LINE__);
401 
402     // Handling for this exception is complete.
403     _Unwind_DeleteException(&header.unwindHeader);
404 
405     return objectp;
406 }
407 
408 extern(C) void _d_throw_exception(Throwable object)
409 {
410     _d_throw(object);
411 }
412 
413 /**
414 * Perform a throw, D style. Throw will unwind through this call,
415 * so there better not be any handlers or exception thrown here.
416 */
417 extern(C) void _d_throw(Throwable object)
418 {
419     // If possible, avoid always allocating new memory for exception headers.
420     ExceptionHeader *eh = ExceptionHeader.create(object);
421 
422     // Add to thrown exception stack.
423     eh.push();
424 
425     // Called by unwinder when exception object needs destruction by other than our code.
426     extern(C) void exception_cleanup(_Unwind_Reason_Code code, _Unwind_Exception* exc)
427     {
428         // If we haven't been caught by a foreign handler, then this is
429         // some sort of unwind error.  In that case just die immediately.
430         // _Unwind_DeleteException in the HP-UX IA64 libunwind library
431         //  returns _URC_NO_REASON and not _URC_FOREIGN_EXCEPTION_CAUGHT
432         // like the GCC _Unwind_DeleteException function does.
433         if (code != _URC_FOREIGN_EXCEPTION_CAUGHT && code != _URC_NO_REASON)
434             terminate("uncaught exception", __LINE__);
435 
436         auto eh = ExceptionHeader.toExceptionHeader(exc);
437         ExceptionHeader.free(eh);
438     }
439 
440     eh.unwindHeader.exception_cleanup = &exception_cleanup;
441 
442     // Runtime now expects us to do this first before unwinding.
443     
444 	//_d_createTrace(eh.object, null);
445 
446     // We're happy with setjmp/longjmp exceptions or region-based
447     // exception handlers: entry points are provided here for both.
448     _Unwind_Reason_Code r = void;
449 
450     version (GNU_SjLj_Exceptions)
451         r = _Unwind_SjLj_RaiseException(&eh.unwindHeader);
452     else
453         r = _Unwind_RaiseException(&eh.unwindHeader);
454 
455     // If code == _URC_END_OF_STACK, then we reached top of stack without finding
456     // a handler for the exception.  Since each thread is run in a try/catch,
457     // this oughtn't happen.  If code is something else, we encountered some sort
458     // of heinous lossage from which we could not recover.  As is the way of such
459     // things, almost certainly we will have crashed before now, rather than
460     // actually being able to diagnose the problem.
461     if (r == _URC_END_OF_STACK)
462     {
463         __gdc_begin_catch(&eh.unwindHeader);
464         //_d_print_throwable(object);
465         terminate("uncaught exception", __LINE__);
466     }
467 
468     terminate("unwind error", __LINE__);
469 }
470 
471 static if (GNU_ARM_EABI_Unwinder)
472 {
473     enum personality_fn_attributes = "";//attribute("target", ("general-regs-only"));
474 }
475 else
476 {
477     enum personality_fn_attributes = "";
478 }
479 
480 /**
481 * Read and extract information from the LSDA (.gcc_except_table section).
482 */
483 @personality_fn_attributes
484 _Unwind_Reason_Code scanLSDA(const(ubyte)* lsda, _Unwind_Exception_Class exceptionClass,
485                              _Unwind_Action actions, _Unwind_Exception* unwindHeader,
486                              _Unwind_Context* context, _Unwind_Word cfa,
487                              out _Unwind_Ptr landingPad, out int handler)
488 {
489     // If no LSDA, then there are no handlers or cleanups.
490     if (lsda is null)
491         return CONTINUE_UNWINDING(unwindHeader, context);
492 
493     // Parse the LSDA header
494     auto p = lsda;
495 
496     auto Start = (context ? _Unwind_GetRegionStart(context) : 0);
497 
498     // Find @LPStart, the base to which landing pad offsets are relative.
499     ubyte LPStartEncoding = *p++;
500     _Unwind_Ptr LPStart = 0;
501 
502     if (LPStartEncoding != DW_EH_PE_omit)
503         LPStart = read_encoded_value(context, LPStartEncoding, &p);
504     else
505         LPStart = Start;
506 
507     // Find @TType, the base of the handler and exception spec type data.
508     ubyte TTypeEncoding = *p++;
509     const(ubyte)* TType = null;
510 
511     if (TTypeEncoding != DW_EH_PE_omit)
512     {
513         static if (__traits(compiles, _TTYPE_ENCODING))
514         {
515             // Older ARM EABI toolchains set this value incorrectly, so use a
516             // hardcoded OS-specific format.
517             TTypeEncoding = _TTYPE_ENCODING;
518         }
519         auto TTbase = read_uleb128(&p);
520         TType = p + TTbase;
521     }
522 
523     // The encoding and length of the call-site table; the action table
524     // immediately follows.
525     ubyte CSEncoding = *p++;
526     auto CSTableSize = read_uleb128(&p);
527     const(ubyte)* actionTable = p + CSTableSize;
528 
529     auto TTypeBase = base_of_encoded_value(TTypeEncoding, context);
530 
531     // Get instruction pointer (ip) at start of instruction that threw.
532     version (CRuntime_Glibc)
533     {
534         int ip_before_insn;
535         auto ip = _Unwind_GetIPInfo(context, &ip_before_insn);
536         if (!ip_before_insn)
537             --ip;
538     }
539     else
540     {
541         auto ip = _Unwind_GetIP(context);
542         --ip;
543     }
544 
545     bool saw_cleanup = false;
546     bool saw_handler = false;
547     const(ubyte)* actionRecord = null;
548 
549     version (GNU_SjLj_Exceptions)
550     {
551         // The given "IP" is an index into the call-site table, with two
552         // exceptions -- -1 means no-action, and 0 means terminate.
553         // But since we're using uleb128 values, we've not got random
554         // access to the array.
555         if (cast(int) ip <= 0)
556         {
557             return _URC_CONTINUE_UNWIND;
558         }
559         else
560         {
561             _uleb128_t CSLandingPad, CSAction;
562             do
563             {
564                 CSLandingPad = read_uleb128(p);
565                 CSAction = read_uleb128(p);
566             }
567             while (--ip);
568 
569             // Can never have null landing pad for sjlj -- that would have
570             // been indicated by a -1 call site index.
571             landingPad = CSLandingPad + 1;
572             if (CSAction)
573                 actionRecord = actionTable + CSAction - 1;
574         }
575     }
576     else
577     {
578         // Search the call-site table for the action associated with this IP.
579         while (p < actionTable)
580         {
581             // Note that all call-site encodings are "absolute" displacements.
582             auto CSStart = read_encoded_value(null, CSEncoding, &p);
583             auto CSLen = read_encoded_value(null, CSEncoding, &p);
584             auto CSLandingPad = read_encoded_value(null, CSEncoding, &p);
585             auto CSAction = read_uleb128(&p);
586 
587             // The table is sorted, so if we've passed the ip, stop.
588             if (ip < Start + CSStart)
589                 p = actionTable;
590             else if (ip < Start + CSStart + CSLen)
591             {
592                 if (CSLandingPad)
593                     landingPad = LPStart + CSLandingPad;
594                 if (CSAction)
595                     actionRecord = actionTable + CSAction - 1;
596                 break;
597             }
598         }
599     }
600 
601     if (landingPad == 0)
602     {
603         // IP is present, but has a null landing pad.
604         // No cleanups or handlers to be run.
605     }
606     else if (actionRecord is null)
607     {
608         // If ip is present, has a non-null landing pad, and a null
609         // action table offset, then there are only cleanups present.
610         // Cleanups use a zero switch value, as set above.
611         saw_cleanup = true;
612     }
613     else
614     {
615         // Otherwise we have a catch handler or exception specification.
616         handler = actionTableLookup(actions, unwindHeader, actionRecord,
617                                     lsda, exceptionClass, TTypeBase,
618                                     TType, TTypeEncoding,
619                                     saw_handler, saw_cleanup);
620     }
621 
622     // IP is not in table.  No associated cleanups.
623     if (!saw_handler && !saw_cleanup)
624         return CONTINUE_UNWINDING(unwindHeader, context);
625 
626     if (actions & _UA_SEARCH_PHASE)
627     {
628         if (!saw_handler)
629             return CONTINUE_UNWINDING(unwindHeader, context);
630 
631         // For domestic exceptions, we cache data from phase 1 for phase 2.
632         if (isGdcExceptionClass(exceptionClass))
633             ExceptionHeader.save(unwindHeader, cfa, handler, lsda, landingPad);
634 
635         return _URC_HANDLER_FOUND;
636     }
637 
638     return 0;
639 }
640 
641 /**
642 * Look up and return the handler index of the classType in Action Table.
643 */
644 int actionTableLookup(_Unwind_Action actions, _Unwind_Exception* unwindHeader,
645                       const(ubyte)* actionRecord, const(ubyte)* lsda,
646                       _Unwind_Exception_Class exceptionClass,
647                       _Unwind_Ptr TTypeBase, const(ubyte)* TType,
648                       ubyte TTypeEncoding,
649                       out bool saw_handler, out bool saw_cleanup)
650 {
651     ClassInfo thrownType;
652     if (isGdcExceptionClass(exceptionClass))
653     {
654         thrownType = getClassInfo(unwindHeader, lsda);
655     }
656 
657     while (1)
658     {
659         auto ap = actionRecord;
660         auto ARFilter = read_sleb128(&ap);
661         auto apn = ap;
662         auto ARDisp = read_sleb128(&ap);
663 
664         if (ARFilter == 0)
665         {
666             // Zero filter values are cleanups.
667             saw_cleanup = true;
668         }
669         else if (actions & _UA_FORCE_UNWIND)
670         {
671             // During forced unwinding, we only run cleanups.
672         }
673         else if (ARFilter > 0)
674         {
675             // Positive filter values are handlers.
676             auto encodedSize = size_of_encoded_value(TTypeEncoding);
677 
678             // ARFilter is the negative index from TType, which is where
679             // the ClassInfo is stored.
680             const(ubyte)* tp = TType - ARFilter * encodedSize;
681 
682             auto entry = read_encoded_value_with_base(TTypeEncoding, TTypeBase, &tp);
683             ClassInfo ci = cast(ClassInfo)cast(void*)(entry);
684 
685             // D does not have catch-all handlers, and so the following
686             // assumes that we will never handle a null value.
687             assert(ci !is null);
688 
689             if (ci.classinfo is __cpp_type_info_ptr.classinfo
690                 && isGxxExceptionClass(exceptionClass))
691             {
692                 // catchType is the catch clause type_info.
693                 auto catchType = cast(CxxTypeInfo)((cast(__cpp_type_info_ptr)cast(void*)ci).ptr);
694                 auto thrownPtr = CxaExceptionHeader.getAdjustedPtr(unwindHeader, catchType);
695 
696                 if (thrownPtr !is null)
697                 {
698                     if (actions & _UA_SEARCH_PHASE)
699                         CxaExceptionHeader.save(unwindHeader, thrownPtr);
700                     saw_handler = true;
701                     return cast(int)ARFilter;
702                 }
703             }
704             else if (isGdcExceptionClass(exceptionClass)
705                      && _d_isbaseof(thrownType, ci))
706             {
707                 saw_handler = true;
708                 return cast(int)ARFilter;
709             }
710             else
711             {
712                 // ??? What to do about other GNU language exceptions.
713             }
714         }
715         else
716         {
717             // Negative filter values are exception specifications,
718             // which D does not use.
719             break;
720         }
721 
722         if (ARDisp == 0)
723             break;
724         actionRecord = apn + ARDisp;
725     }
726 
727     return 0;
728 }
729 
730 /**
731 * Look at the chain of inflight exceptions and pick the class type that'll
732 * be looked for in catch clauses.
733 */
734 ClassInfo getClassInfo(_Unwind_Exception* unwindHeader,
735                        const(ubyte)* currentLsd) @nogc
736 					   {
737 						ExceptionHeader* eh = ExceptionHeader.toExceptionHeader(unwindHeader);
738 						// The first thrown Exception at the top of the stack takes precedence
739 						// over others that are inflight, unless an Error was thrown, in which
740 						// case, we search for error handlers instead.
741 						Throwable ehobject = eh.object;
742 						for (ExceptionHeader* ehn = eh.next; ehn; ehn = ehn.next)
743 						{
744 							const(ubyte)* nextLsd = void;
745 							_Unwind_Ptr nextLandingPad = void;
746 							_Unwind_Word nextCfa = void;
747 							int nextHandler = void;
748 
749 							ExceptionHeader.restore(&ehn.unwindHeader, nextHandler, nextLsd, nextLandingPad, nextCfa);
750 
751 							// Don't combine when the exceptions are from different functions.
752 							if (currentLsd != nextLsd)
753 								break;
754 
755 							Error e = cast(Error)ehobject;
756 							if (e is null || (cast(Error)ehn.object) !is null)
757 							{
758 								currentLsd = nextLsd;
759 								ehobject = ehn.object;
760 							}
761 						}
762 						return ehobject.classinfo;
763 					   }
764 
765 /**
766 * Called when the personality function has found neither a cleanup or handler.
767 * To support ARM EABI personality routines, that must also unwind the stack.
768 */
769 @personality_fn_attributes
770 _Unwind_Reason_Code CONTINUE_UNWINDING(_Unwind_Exception* unwindHeader, _Unwind_Context* context)
771 {
772     static if (GNU_ARM_EABI_Unwinder)
773     {
774         if (__gnu_unwind_frame(unwindHeader, context) != _URC_OK)
775             return _URC_FAILURE;
776     }
777     return _URC_CONTINUE_UNWIND;
778 }
779 
780 /**
781 * Using a different personality function name causes link failures
782 * when trying to mix code using different exception handling models.
783 */
784 version (GNU_SEH_Exceptions)
785 {
786     enum PERSONALITY_FUNCTION = "__gdc_personality_imp";
787 
788     extern(C) EXCEPTION_DISPOSITION __gdc_personality_seh0(void* ms_exc, void* this_frame,
789                                                            void* ms_orig_context, void* ms_disp)
790     {
791         return _GCC_specific_handler(ms_exc, this_frame, ms_orig_context,
792                                      ms_disp, &gdc_personality);
793     }
794 }
795 else version (GNU_SjLj_Exceptions)
796 {
797     enum PERSONALITY_FUNCTION = "__gdc_personality_sj0";
798 
799     private int __builtin_eh_return_data_regno(int x) { return x; }
800 }
801 else
802 {
803     enum PERSONALITY_FUNCTION = "__gdc_personality_v0";
804 }
805 
806 extern(C) _Unwind_Reason_Code _d_eh_personality(_Unwind_State state, _Unwind_Exception* unwindHeader, _Unwind_Context* context)
807 {
808     return gdc_personality(state, unwindHeader, context);
809 }
810 
811 /**
812 * The "personality" function, specific to each language.
813 */
814 static if (GNU_ARM_EABI_Unwinder)
815 {
816     pragma(mangle, PERSONALITY_FUNCTION)
817 		@personality_fn_attributes
818 		extern(C) _Unwind_Reason_Code gdc_personality(_Unwind_State state,
819 													  _Unwind_Exception* unwindHeader,
820 													  _Unwind_Context* context)
821 		{
822 			_Unwind_Action actions;
823 
824 			switch (state & _US_ACTION_MASK)
825 			{
826 				case _US_VIRTUAL_UNWIND_FRAME:
827 					// If the unwind state pattern is (_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND)
828 					// then we don't need to search for any handler as it is not a real exception.
829 					// Just unwind the stack.
830 					if (state & _US_FORCE_UNWIND)
831 						return CONTINUE_UNWINDING(unwindHeader, context);
832 					actions = _UA_SEARCH_PHASE;
833 					break;
834 
835 				case _US_UNWIND_FRAME_STARTING:
836 					actions = _UA_CLEANUP_PHASE;
837 					if (!(state & _US_FORCE_UNWIND)
838 						&& unwindHeader.barrier_cache.sp == _Unwind_GetGR(context, UNWIND_STACK_REG))
839 						actions |= _UA_HANDLER_FRAME;
840 					break;
841 
842 				case _US_UNWIND_FRAME_RESUME:
843 					return CONTINUE_UNWINDING(unwindHeader, context);
844 
845 				default:
846 					terminate("unwind error", __LINE__);
847 			}
848 			actions |= state & _US_FORCE_UNWIND;
849 
850 			// The dwarf unwinder assumes the context structure holds things like
851 			// the function and LSDA pointers.  The ARM implementation caches these
852 			// in the exception header (UCB).  To avoid rewriting everything we make
853 			// the virtual IP register point at the UCB.
854 			_Unwind_SetGR(context, UNWIND_POINTER_REG, cast(_Unwind_Ptr)unwindHeader);
855 
856 			return __gdc_personality(actions, unwindHeader.exception_class,
857 									 unwindHeader, context);
858 		}
859 }
860 else
861 {
862     pragma(mangle, PERSONALITY_FUNCTION)
863 		extern(C) _Unwind_Reason_Code gdc_personality(int iversion,
864 													  _Unwind_Action actions,
865 													  _Unwind_Exception_Class exceptionClass,
866 													  _Unwind_Exception* unwindHeader,
867 													  _Unwind_Context* context)
868 		{
869 			// Interface version check.
870 			if (iversion != 1)
871 				return _URC_FATAL_PHASE1_ERROR;
872 
873 			return __gdc_personality(actions, exceptionClass, unwindHeader, context);
874 		}
875 }
876 
877 @personality_fn_attributes
878 private _Unwind_Reason_Code __gdc_personality(_Unwind_Action actions,
879                                               _Unwind_Exception_Class exceptionClass,
880                                               _Unwind_Exception* unwindHeader,
881                                               _Unwind_Context* context)
882 {
883     const(ubyte)* lsda;
884     _Unwind_Ptr landingPad;
885     _Unwind_Word cfa;
886     int handler;
887 
888     // Shortcut for phase 2 found handler for domestic exception.
889     if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
890         && isGdcExceptionClass(exceptionClass))
891     {
892         ExceptionHeader.restore(unwindHeader, handler, lsda, landingPad, cfa);
893         // Shouldn't have cached a null landing pad in phase 1.
894         if (landingPad == 0)
895             terminate("unwind error", __LINE__);
896     }
897     else
898     {
899         lsda = cast(ubyte*)_Unwind_GetLanguageSpecificData(context);
900 
901         static if (GNU_ARM_EABI_Unwinder)
902             cfa = _Unwind_GetGR(context, UNWIND_STACK_REG);
903         else
904             cfa = _Unwind_GetCFA(context);
905 
906         auto result = scanLSDA(lsda, exceptionClass, actions, unwindHeader,
907                                context, cfa, landingPad, handler);
908 
909         // Positive on handler found in phase 1, continue unwinding, or failure.
910         if (result)
911             return result;
912     }
913 
914     // Unexpected negative handler, call terminate directly.
915     if (handler < 0)
916         terminate("unwind error", __LINE__);
917 
918     // We can't use any of the deh routines with foreign exceptions,
919     // because they all expect unwindHeader to be an ExceptionHeader.
920     if (isGdcExceptionClass(exceptionClass))
921     {
922         // If there are any in-flight exceptions being thrown, chain our
923         // current object onto the end of the prevous object.
924         ExceptionHeader* eh = ExceptionHeader.toExceptionHeader(unwindHeader);
925         auto currentLsd = lsda;
926         bool bypassed = false;
927 
928         while (eh.next)
929         {
930             ExceptionHeader* ehn = eh.next;
931             const(ubyte)* nextLsd = void;
932             _Unwind_Ptr nextLandingPad = void;
933             _Unwind_Word nextCfa = void;
934             int nextHandler = void;
935 
936             ExceptionHeader.restore(&ehn.unwindHeader, nextHandler, nextLsd, nextLandingPad, nextCfa);
937 
938             Error e = cast(Error)eh.object;
939             if (e !is null && !cast(Error)ehn.object)
940             {
941                 // We found an Error, bypass the exception chain.
942                 currentLsd = nextLsd;
943                 eh = ehn;
944                 bypassed = true;
945                 continue;
946             }
947 
948             // Don't combine when the exceptions are from different functions.
949             if (currentLsd != nextLsd)
950                 break;
951 
952             // Add our object onto the end of the existing chain.
953             Throwable n = ehn.object;
954             while (n.next)
955                 n = n.next;
956             n.next = eh.object;
957 
958             // Replace our exception object with in-flight one
959             eh.object = ehn.object;
960             if (nextHandler != handler && !bypassed)
961             {
962                 handler = nextHandler;
963                 ExceptionHeader.save(unwindHeader, cfa, handler, lsda, landingPad);
964             }
965 
966             // Exceptions chained, can now throw away the previous header.
967             eh.next = ehn.next;
968             _Unwind_DeleteException(&ehn.unwindHeader);
969         }
970 
971         if (bypassed)
972         {
973             eh = ExceptionHeader.toExceptionHeader(unwindHeader);
974             Error e = cast(Error)eh.object;
975             auto ehn = eh.next;
976             e.bypassedException = ehn.object;
977             eh.next = ehn.next;
978             _Unwind_DeleteException(&ehn.unwindHeader);
979         }
980     }
981 
982     // Set up registers and jump to cleanup or handler.
983     // For targets with pointers smaller than the word size, we must extend the
984     // pointer, and this extension is target dependent.
985     _Unwind_SetGR(context, 0,
986                   cast(_Unwind_Ptr)unwindHeader);
987     _Unwind_SetGR(context, 1, handler);
988     _Unwind_SetIP(context, landingPad);
989 
990     return _URC_INSTALL_CONTEXT;
991 }