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