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