xref: /netbsd-src/external/gpl3/gcc.old/dist/libphobos/libdruntime/gcc/deh.d (revision 4c3eb207d36f67d31994830c0a694161fc1ca39b)
1627f7eb2Smrg // GNU D Compiler exception personality routines.
2*4c3eb207Smrg // Copyright (C) 2011-2020 Free Software Foundation, Inc.
3627f7eb2Smrg 
4627f7eb2Smrg // GCC is free software; you can redistribute it and/or modify it under
5627f7eb2Smrg // the terms of the GNU General Public License as published by the Free
6627f7eb2Smrg // Software Foundation; either version 3, or (at your option) any later
7627f7eb2Smrg // version.
8627f7eb2Smrg 
9627f7eb2Smrg // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
10627f7eb2Smrg // WARRANTY; without even the implied warranty of MERCHANTABILITY or
11627f7eb2Smrg // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12627f7eb2Smrg // for more details.
13627f7eb2Smrg 
14627f7eb2Smrg // Under Section 7 of GPL version 3, you are granted additional
15627f7eb2Smrg // permissions described in the GCC Runtime Library Exception, version
16627f7eb2Smrg // 3.1, as published by the Free Software Foundation.
17627f7eb2Smrg 
18627f7eb2Smrg // You should have received a copy of the GNU General Public License and
19627f7eb2Smrg // a copy of the GCC Runtime Library Exception along with this program;
20627f7eb2Smrg // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
21627f7eb2Smrg // <http://www.gnu.org/licenses/>.
22627f7eb2Smrg 
23627f7eb2Smrg // This code is based on the libstdc++ exception handling routines.
24627f7eb2Smrg 
25627f7eb2Smrg module gcc.deh;
26627f7eb2Smrg 
27627f7eb2Smrg import gcc.unwind;
28627f7eb2Smrg import gcc.unwind.pe;
29627f7eb2Smrg import gcc.builtins;
30627f7eb2Smrg import gcc.config;
31627f7eb2Smrg import gcc.attribute;
32627f7eb2Smrg 
33627f7eb2Smrg extern(C)
34627f7eb2Smrg {
35627f7eb2Smrg     int _d_isbaseof(ClassInfo, ClassInfo);
36627f7eb2Smrg     void _d_createTrace(Object, void*);
37627f7eb2Smrg 
38627f7eb2Smrg     // Not used in GDC but declaration required by rt/sections.d
39627f7eb2Smrg     struct FuncTable
40627f7eb2Smrg     {
41627f7eb2Smrg     }
42627f7eb2Smrg }
43627f7eb2Smrg 
44627f7eb2Smrg /**
45627f7eb2Smrg  * Declare all known and handled exception classes.
46627f7eb2Smrg  * D exceptions -- "GNUCD\0\0\0".
47627f7eb2Smrg  * C++ exceptions -- "GNUCC++\0"
48627f7eb2Smrg  * C++ dependent exceptions -- "GNUCC++\x01"
49627f7eb2Smrg  */
50627f7eb2Smrg static if (GNU_ARM_EABI_Unwinder)
51627f7eb2Smrg {
52627f7eb2Smrg     enum _Unwind_Exception_Class gdcExceptionClass = "GNUCD\0\0\0";
53627f7eb2Smrg     enum _Unwind_Exception_Class gxxExceptionClass = "GNUCC++\0";
54627f7eb2Smrg     enum _Unwind_Exception_Class gxxDependentExceptionClass = "GNUCC++\x01";
55627f7eb2Smrg }
56627f7eb2Smrg else
57627f7eb2Smrg {
58627f7eb2Smrg     enum _Unwind_Exception_Class gdcExceptionClass =
59627f7eb2Smrg         (cast(_Unwind_Exception_Class)'G' << 56) |
60627f7eb2Smrg         (cast(_Unwind_Exception_Class)'N' << 48) |
61627f7eb2Smrg         (cast(_Unwind_Exception_Class)'U' << 40) |
62627f7eb2Smrg         (cast(_Unwind_Exception_Class)'C' << 32) |
63627f7eb2Smrg         (cast(_Unwind_Exception_Class)'D' << 24);
64627f7eb2Smrg 
65627f7eb2Smrg     enum _Unwind_Exception_Class gxxExceptionClass =
66627f7eb2Smrg         (cast(_Unwind_Exception_Class)'G' << 56) |
67627f7eb2Smrg         (cast(_Unwind_Exception_Class)'N' << 48) |
68627f7eb2Smrg         (cast(_Unwind_Exception_Class)'U' << 40) |
69627f7eb2Smrg         (cast(_Unwind_Exception_Class)'C' << 32) |
70627f7eb2Smrg         (cast(_Unwind_Exception_Class)'C' << 24) |
71627f7eb2Smrg         (cast(_Unwind_Exception_Class)'+' << 16) |
72627f7eb2Smrg         (cast(_Unwind_Exception_Class)'+' <<  8) |
73627f7eb2Smrg         (cast(_Unwind_Exception_Class)0 <<  0);
74627f7eb2Smrg 
75627f7eb2Smrg     enum _Unwind_Exception_Class gxxDependentExceptionClass =
76627f7eb2Smrg         gxxExceptionClass + 1;
77627f7eb2Smrg }
78627f7eb2Smrg 
79627f7eb2Smrg /**
80627f7eb2Smrg  * Checks for GDC exception class.
81627f7eb2Smrg  */
82627f7eb2Smrg bool isGdcExceptionClass(_Unwind_Exception_Class c) @nogc
83627f7eb2Smrg {
84627f7eb2Smrg     static if (GNU_ARM_EABI_Unwinder)
85627f7eb2Smrg     {
86627f7eb2Smrg         return c[0] == gdcExceptionClass[0]
87627f7eb2Smrg             && c[1] == gdcExceptionClass[1]
88627f7eb2Smrg             && c[2] == gdcExceptionClass[2]
89627f7eb2Smrg             && c[3] == gdcExceptionClass[3]
90627f7eb2Smrg             && c[4] == gdcExceptionClass[4]
91627f7eb2Smrg             && c[5] == gdcExceptionClass[5]
92627f7eb2Smrg             && c[6] == gdcExceptionClass[6]
93627f7eb2Smrg             && c[7] == gdcExceptionClass[7];
94627f7eb2Smrg     }
95627f7eb2Smrg     else
96627f7eb2Smrg     {
97627f7eb2Smrg         return c == gdcExceptionClass;
98627f7eb2Smrg     }
99627f7eb2Smrg }
100627f7eb2Smrg 
101627f7eb2Smrg /**
102627f7eb2Smrg  * Checks for any C++ exception class.
103627f7eb2Smrg  */
104627f7eb2Smrg bool isGxxExceptionClass(_Unwind_Exception_Class c) @nogc
105627f7eb2Smrg {
106627f7eb2Smrg     static if (GNU_ARM_EABI_Unwinder)
107627f7eb2Smrg     {
108627f7eb2Smrg         return c[0] == gxxExceptionClass[0]
109627f7eb2Smrg             && c[1] == gxxExceptionClass[1]
110627f7eb2Smrg             && c[2] == gxxExceptionClass[2]
111627f7eb2Smrg             && c[3] == gxxExceptionClass[3]
112627f7eb2Smrg             && c[4] == gxxExceptionClass[4]
113627f7eb2Smrg             && c[5] == gxxExceptionClass[5]
114627f7eb2Smrg             && c[6] == gxxExceptionClass[6]
115627f7eb2Smrg             && (c[7] == gxxExceptionClass[7]
116627f7eb2Smrg                 || c[7] == gxxDependentExceptionClass[7]);
117627f7eb2Smrg     }
118627f7eb2Smrg     else
119627f7eb2Smrg     {
120627f7eb2Smrg         return c == gxxExceptionClass
121627f7eb2Smrg             || c == gxxDependentExceptionClass;
122627f7eb2Smrg     }
123627f7eb2Smrg }
124627f7eb2Smrg 
125627f7eb2Smrg /**
126627f7eb2Smrg  * Checks for primary or dependent, but not that it is a C++ exception.
127627f7eb2Smrg  */
128627f7eb2Smrg bool isDependentException(_Unwind_Exception_Class c) @nogc
129627f7eb2Smrg {
130627f7eb2Smrg     static if (GNU_ARM_EABI_Unwinder)
131627f7eb2Smrg         return (c[7] == '\x01');
132627f7eb2Smrg     else
133627f7eb2Smrg         return (c & 1);
134627f7eb2Smrg }
135627f7eb2Smrg 
136627f7eb2Smrg /**
137627f7eb2Smrg  * A D exception object consists of a header, which is a wrapper
138627f7eb2Smrg  * around an unwind object header with additional D specific
139627f7eb2Smrg  * information, prefixed by the exception object itself.
140627f7eb2Smrg  */
141627f7eb2Smrg struct ExceptionHeader
142627f7eb2Smrg {
143627f7eb2Smrg     // Because of a lack of __aligned__ style attribute, our object
144627f7eb2Smrg     // and the unwind object are the first two fields.
145627f7eb2Smrg     static if (Throwable.alignof < _Unwind_Exception.alignof)
146627f7eb2Smrg         ubyte[_Unwind_Exception.alignof - Throwable.alignof] pad;
147627f7eb2Smrg 
148627f7eb2Smrg     // The object being thrown.  The compiled code expects this to
149627f7eb2Smrg     // be immediately before the generic exception header.
150627f7eb2Smrg     Throwable object;
151627f7eb2Smrg 
152627f7eb2Smrg     // The generic exception header.
153627f7eb2Smrg     _Unwind_Exception unwindHeader;
154627f7eb2Smrg 
155627f7eb2Smrg     static assert(unwindHeader.offsetof - object.offsetof == object.sizeof);
156627f7eb2Smrg 
157627f7eb2Smrg     // Cache handler details between Phase 1 and Phase 2.
158627f7eb2Smrg     static if (GNU_ARM_EABI_Unwinder)
159627f7eb2Smrg     {
160627f7eb2Smrg         // Nothing here yet.
161627f7eb2Smrg     }
162627f7eb2Smrg     else
163627f7eb2Smrg     {
164627f7eb2Smrg         // Which catch was found.
165627f7eb2Smrg         int handler;
166627f7eb2Smrg 
167627f7eb2Smrg         // Language Specific Data Area for function enclosing the handler.
168627f7eb2Smrg         const(ubyte)* languageSpecificData;
169627f7eb2Smrg 
170627f7eb2Smrg         // Pointer to catch code.
171627f7eb2Smrg         _Unwind_Ptr landingPad;
172627f7eb2Smrg 
173627f7eb2Smrg         // Canonical Frame Address (CFA) for the enclosing handler.
174627f7eb2Smrg         _Unwind_Word canonicalFrameAddress;
175627f7eb2Smrg     }
176627f7eb2Smrg 
177627f7eb2Smrg     // Stack other thrown exceptions in current thread through here.
178627f7eb2Smrg     ExceptionHeader* next;
179627f7eb2Smrg 
180627f7eb2Smrg     // Thread local stack of chained exceptions.
181627f7eb2Smrg     static ExceptionHeader* stack;
182627f7eb2Smrg 
183627f7eb2Smrg     // Pre-allocate storage for 1 instance per thread.
184627f7eb2Smrg     // Use calloc/free for multiple exceptions in flight.
185627f7eb2Smrg     static ExceptionHeader ehstorage;
186627f7eb2Smrg 
187627f7eb2Smrg     /**
188627f7eb2Smrg      * Allocate and initialize an ExceptionHeader.
189627f7eb2Smrg      */
190627f7eb2Smrg     static ExceptionHeader* create(Throwable o) @nogc
191627f7eb2Smrg     {
192627f7eb2Smrg         auto eh = &ehstorage;
193627f7eb2Smrg 
194627f7eb2Smrg         // Check exception object in use.
195627f7eb2Smrg         if (eh.object)
196627f7eb2Smrg         {
197627f7eb2Smrg             eh = cast(ExceptionHeader*) __builtin_calloc(ExceptionHeader.sizeof, 1);
198627f7eb2Smrg             // Out of memory while throwing - not much else can be done.
199627f7eb2Smrg             if (!eh)
200627f7eb2Smrg                 terminate("out of memory", __LINE__);
201627f7eb2Smrg         }
202627f7eb2Smrg         eh.object = o;
203627f7eb2Smrg 
204627f7eb2Smrg         eh.unwindHeader.exception_class = gdcExceptionClass;
205627f7eb2Smrg 
206627f7eb2Smrg         return eh;
207627f7eb2Smrg     }
208627f7eb2Smrg 
209627f7eb2Smrg     /**
210627f7eb2Smrg      * Free ExceptionHeader that was created by create().
211627f7eb2Smrg      */
212627f7eb2Smrg     static void free(ExceptionHeader* eh) @nogc
213627f7eb2Smrg     {
214627f7eb2Smrg         *eh = ExceptionHeader.init;
215627f7eb2Smrg         if (eh != &ehstorage)
216627f7eb2Smrg             __builtin_free(eh);
217627f7eb2Smrg     }
218627f7eb2Smrg 
219627f7eb2Smrg     /**
220627f7eb2Smrg      * Push this onto stack of chained exceptions.
221627f7eb2Smrg      */
222627f7eb2Smrg     void push() @nogc
223627f7eb2Smrg     {
224627f7eb2Smrg         next = stack;
225627f7eb2Smrg         stack = &this;
226627f7eb2Smrg     }
227627f7eb2Smrg 
228627f7eb2Smrg     /**
229627f7eb2Smrg      * Pop and return top of chained exception stack.
230627f7eb2Smrg      */
231627f7eb2Smrg     static ExceptionHeader* pop() @nogc
232627f7eb2Smrg     {
233627f7eb2Smrg         auto eh = stack;
234627f7eb2Smrg         stack = eh.next;
235627f7eb2Smrg         return eh;
236627f7eb2Smrg     }
237627f7eb2Smrg 
238627f7eb2Smrg     /**
239627f7eb2Smrg      * Save stage1 handler information in the exception object.
240627f7eb2Smrg      */
241627f7eb2Smrg     static void save(_Unwind_Exception* unwindHeader,
242627f7eb2Smrg                      _Unwind_Word cfa, int handler,
243627f7eb2Smrg                      const(ubyte)* lsda, _Unwind_Ptr landingPad) @nogc
244627f7eb2Smrg     {
245627f7eb2Smrg         static if (GNU_ARM_EABI_Unwinder)
246627f7eb2Smrg         {
247627f7eb2Smrg             unwindHeader.barrier_cache.sp = cfa;
248627f7eb2Smrg             unwindHeader.barrier_cache.bitpattern[1] = cast(_uw)handler;
249627f7eb2Smrg             unwindHeader.barrier_cache.bitpattern[2] = cast(_uw)lsda;
250627f7eb2Smrg             unwindHeader.barrier_cache.bitpattern[3] = cast(_uw)landingPad;
251627f7eb2Smrg         }
252627f7eb2Smrg         else
253627f7eb2Smrg         {
254627f7eb2Smrg             ExceptionHeader* eh = toExceptionHeader(unwindHeader);
255627f7eb2Smrg             eh.canonicalFrameAddress = cfa;
256627f7eb2Smrg             eh.handler = handler;
257627f7eb2Smrg             eh.languageSpecificData = lsda;
258627f7eb2Smrg             eh.landingPad = landingPad;
259627f7eb2Smrg         }
260627f7eb2Smrg     }
261627f7eb2Smrg 
262627f7eb2Smrg     /**
263627f7eb2Smrg      * Restore the catch handler data saved during phase1.
264627f7eb2Smrg      */
265627f7eb2Smrg     static void restore(_Unwind_Exception* unwindHeader, out int handler,
266627f7eb2Smrg                         out const(ubyte)* lsda, out _Unwind_Ptr landingPad,
267627f7eb2Smrg                         out _Unwind_Word cfa) @nogc
268627f7eb2Smrg     {
269627f7eb2Smrg         static if (GNU_ARM_EABI_Unwinder)
270627f7eb2Smrg         {
271627f7eb2Smrg             cfa = unwindHeader.barrier_cache.sp;
272627f7eb2Smrg             handler = cast(int)unwindHeader.barrier_cache.bitpattern[1];
273627f7eb2Smrg             lsda = cast(ubyte*)unwindHeader.barrier_cache.bitpattern[2];
274627f7eb2Smrg             landingPad = cast(_Unwind_Ptr)unwindHeader.barrier_cache.bitpattern[3];
275627f7eb2Smrg         }
276627f7eb2Smrg         else
277627f7eb2Smrg         {
278627f7eb2Smrg             ExceptionHeader* eh = toExceptionHeader(unwindHeader);
279627f7eb2Smrg             cfa = eh.canonicalFrameAddress;
280627f7eb2Smrg             handler = eh.handler;
281627f7eb2Smrg             lsda = eh.languageSpecificData;
282627f7eb2Smrg             landingPad = cast(_Unwind_Ptr)eh.landingPad;
283627f7eb2Smrg         }
284627f7eb2Smrg     }
285627f7eb2Smrg 
286627f7eb2Smrg     /**
287627f7eb2Smrg      * Look at the chain of inflight exceptions and pick the class type that'll
288627f7eb2Smrg      * be looked for in catch clauses.
289627f7eb2Smrg      */
290627f7eb2Smrg     static ClassInfo getClassInfo(_Unwind_Exception* unwindHeader) @nogc
291627f7eb2Smrg     {
292627f7eb2Smrg         ExceptionHeader* eh = toExceptionHeader(unwindHeader);
293627f7eb2Smrg         // The first thrown Exception at the top of the stack takes precedence
294627f7eb2Smrg         // over others that are inflight, unless an Error was thrown, in which
295627f7eb2Smrg         // case, we search for error handlers instead.
296627f7eb2Smrg         Throwable ehobject = eh.object;
297627f7eb2Smrg         for (ExceptionHeader* ehn = eh.next; ehn; ehn = ehn.next)
298627f7eb2Smrg         {
299627f7eb2Smrg             Error e = cast(Error)ehobject;
300627f7eb2Smrg             if (e is null || (cast(Error)ehn.object) !is null)
301627f7eb2Smrg                 ehobject = ehn.object;
302627f7eb2Smrg         }
303627f7eb2Smrg         return ehobject.classinfo;
304627f7eb2Smrg     }
305627f7eb2Smrg 
306627f7eb2Smrg     /**
307627f7eb2Smrg      * Convert from pointer to unwindHeader to pointer to ExceptionHeader
308627f7eb2Smrg      * that it is embedded inside of.
309627f7eb2Smrg      */
310627f7eb2Smrg     static ExceptionHeader* toExceptionHeader(_Unwind_Exception* exc) @nogc
311627f7eb2Smrg     {
312627f7eb2Smrg         return cast(ExceptionHeader*)(cast(void*)exc - ExceptionHeader.unwindHeader.offsetof);
313627f7eb2Smrg     }
314627f7eb2Smrg }
315627f7eb2Smrg 
316627f7eb2Smrg /**
317627f7eb2Smrg  * Map to C++ std::type_info's virtual functions from D,
318627f7eb2Smrg  * being careful to not require linking with libstdc++.
319627f7eb2Smrg  * So it is given a different name.
320627f7eb2Smrg  */
321627f7eb2Smrg extern(C++) interface CxxTypeInfo
322627f7eb2Smrg {
323627f7eb2Smrg     void dtor1();
324627f7eb2Smrg     void dtor2();
325627f7eb2Smrg     bool __is_pointer_p() const;
326627f7eb2Smrg     bool __is_function_p() const;
327627f7eb2Smrg     bool __do_catch(const CxxTypeInfo, void**, uint) const;
328627f7eb2Smrg     bool __do_upcast(const void*, void**) const;
329627f7eb2Smrg }
330627f7eb2Smrg 
331627f7eb2Smrg /**
332627f7eb2Smrg  * Structure of a C++ exception, represented as a C structure.
333627f7eb2Smrg  * See unwind-cxx.h for the full definition.
334627f7eb2Smrg  */
335627f7eb2Smrg struct CxaExceptionHeader
336627f7eb2Smrg {
337627f7eb2Smrg     union
338627f7eb2Smrg     {
339627f7eb2Smrg         CxxTypeInfo exceptionType;
340627f7eb2Smrg         void* primaryException;
341627f7eb2Smrg     }
342627f7eb2Smrg     void function(void*) exceptionDestructor;
343627f7eb2Smrg     void function() unexpectedHandler;
344627f7eb2Smrg     void function() terminateHandler;
345627f7eb2Smrg     CxaExceptionHeader* nextException;
346627f7eb2Smrg     int handlerCount;
347627f7eb2Smrg 
348627f7eb2Smrg     static if (GNU_ARM_EABI_Unwinder)
349627f7eb2Smrg     {
350627f7eb2Smrg         CxaExceptionHeader* nextPropagatingException;
351627f7eb2Smrg         int propagationCount;
352627f7eb2Smrg     }
353627f7eb2Smrg     else
354627f7eb2Smrg     {
355627f7eb2Smrg         int handlerSwitchValue;
356627f7eb2Smrg         const(ubyte)* actionRecord;
357627f7eb2Smrg         const(ubyte)* languageSpecificData;
358627f7eb2Smrg         _Unwind_Ptr catchTemp;
359627f7eb2Smrg         void* adjustedPtr;
360627f7eb2Smrg     }
361627f7eb2Smrg 
362627f7eb2Smrg     _Unwind_Exception unwindHeader;
363627f7eb2Smrg 
364627f7eb2Smrg     /**
365627f7eb2Smrg      * There's no saving between phases, so only cache pointer.
366627f7eb2Smrg      * __cxa_begin_catch expects this to be set.
367627f7eb2Smrg      */
368627f7eb2Smrg     static void save(_Unwind_Exception* unwindHeader, void* thrownPtr) @nogc
369627f7eb2Smrg     {
370627f7eb2Smrg         static if (GNU_ARM_EABI_Unwinder)
371627f7eb2Smrg             unwindHeader.barrier_cache.bitpattern[0] = cast(_uw) thrownPtr;
372627f7eb2Smrg         else
373627f7eb2Smrg         {
374627f7eb2Smrg             auto eh = toExceptionHeader(unwindHeader);
375627f7eb2Smrg             eh.adjustedPtr = thrownPtr;
376627f7eb2Smrg         }
377627f7eb2Smrg     }
378627f7eb2Smrg 
379627f7eb2Smrg     /**
380627f7eb2Smrg      * Get pointer to the thrown object if the thrown object type behind the
381627f7eb2Smrg      * exception is implicitly convertible to the catch type.
382627f7eb2Smrg      */
383627f7eb2Smrg     static void* getAdjustedPtr(_Unwind_Exception* exc, CxxTypeInfo catchType)
384627f7eb2Smrg     {
385627f7eb2Smrg         void* thrownPtr;
386627f7eb2Smrg 
387627f7eb2Smrg         // A dependent C++ exceptions is just a wrapper around the unwind header.
388627f7eb2Smrg         // A primary C++ exception has the thrown object located immediately after it.
389627f7eb2Smrg         if (isDependentException(exc.exception_class))
390627f7eb2Smrg             thrownPtr = toExceptionHeader(exc).primaryException;
391627f7eb2Smrg         else
392627f7eb2Smrg             thrownPtr = cast(void*)(exc + 1);
393627f7eb2Smrg 
394627f7eb2Smrg         // Pointer types need to adjust the actual pointer, not the pointer that is
395627f7eb2Smrg         // the exception object.  This also has the effect of passing pointer types
396627f7eb2Smrg         // "by value" through the __cxa_begin_catch return value.
397627f7eb2Smrg         const throw_type = (cast(CxaExceptionHeader*)thrownPtr - 1).exceptionType;
398627f7eb2Smrg 
399627f7eb2Smrg         if (throw_type.__is_pointer_p())
400627f7eb2Smrg             thrownPtr = *cast(void**)thrownPtr;
401627f7eb2Smrg 
402627f7eb2Smrg         // Pointer adjustment may be necessary due to multiple inheritance
403627f7eb2Smrg         if (catchType is throw_type
404627f7eb2Smrg             || catchType.__do_catch(throw_type, &thrownPtr, 1))
405627f7eb2Smrg             return thrownPtr;
406627f7eb2Smrg 
407627f7eb2Smrg         return null;
408627f7eb2Smrg     }
409627f7eb2Smrg 
410627f7eb2Smrg     /**
411627f7eb2Smrg      * Convert from pointer to unwindHeader to pointer to CxaExceptionHeader
412627f7eb2Smrg      * that it is embedded inside of.
413627f7eb2Smrg      */
414627f7eb2Smrg     static CxaExceptionHeader* toExceptionHeader(_Unwind_Exception* exc) @nogc
415627f7eb2Smrg     {
416627f7eb2Smrg         return cast(CxaExceptionHeader*)(exc + 1) - 1;
417627f7eb2Smrg     }
418627f7eb2Smrg }
419627f7eb2Smrg 
420627f7eb2Smrg /**
421627f7eb2Smrg  * Called if exception handling must be abandoned for any reason.
422627f7eb2Smrg  */
423627f7eb2Smrg private void terminate(string msg, uint line) @nogc
424627f7eb2Smrg {
425627f7eb2Smrg     import core.stdc.stdio;
426627f7eb2Smrg     import core.stdc.stdlib;
427627f7eb2Smrg 
428627f7eb2Smrg     static bool terminating;
429627f7eb2Smrg     if (terminating)
430627f7eb2Smrg     {
431627f7eb2Smrg         fputs("terminate called recursively\n", stderr);
432627f7eb2Smrg         abort();
433627f7eb2Smrg     }
434627f7eb2Smrg     terminating = true;
435627f7eb2Smrg 
436627f7eb2Smrg     fprintf(stderr, "gcc.deh(%u): %.*s\n", line, cast(int)msg.length, msg.ptr);
437627f7eb2Smrg 
438627f7eb2Smrg     abort();
439627f7eb2Smrg }
440627f7eb2Smrg 
441627f7eb2Smrg /**
442627f7eb2Smrg  * Called when fibers switch contexts.
443627f7eb2Smrg  */
444627f7eb2Smrg extern(C) void* _d_eh_swapContext(void* newContext) nothrow @nogc
445627f7eb2Smrg {
446627f7eb2Smrg     auto old = ExceptionHeader.stack;
447627f7eb2Smrg     ExceptionHeader.stack = cast(ExceptionHeader*)newContext;
448627f7eb2Smrg     return old;
449627f7eb2Smrg }
450627f7eb2Smrg 
451627f7eb2Smrg /**
452627f7eb2Smrg  * Called before starting a catch.  Returns the exception object.
453627f7eb2Smrg  */
454627f7eb2Smrg extern(C) void* __gdc_begin_catch(_Unwind_Exception* unwindHeader)
455627f7eb2Smrg {
456627f7eb2Smrg     ExceptionHeader* header = ExceptionHeader.toExceptionHeader(unwindHeader);
457627f7eb2Smrg 
458627f7eb2Smrg     void* objectp = cast(void*)header.object;
459627f7eb2Smrg 
460627f7eb2Smrg     // Something went wrong when stacking up chained headers...
461627f7eb2Smrg     if (header != ExceptionHeader.pop())
462627f7eb2Smrg         terminate("catch error", __LINE__);
463627f7eb2Smrg 
464627f7eb2Smrg     // Handling for this exception is complete.
465627f7eb2Smrg     _Unwind_DeleteException(&header.unwindHeader);
466627f7eb2Smrg 
467627f7eb2Smrg     return objectp;
468627f7eb2Smrg }
469627f7eb2Smrg 
470627f7eb2Smrg /**
471627f7eb2Smrg  * Perform a throw, D style. Throw will unwind through this call,
472627f7eb2Smrg  * so there better not be any handlers or exception thrown here.
473627f7eb2Smrg  */
474627f7eb2Smrg extern(C) void _d_throw(Throwable object)
475627f7eb2Smrg {
476627f7eb2Smrg     // If possible, avoid always allocating new memory for exception headers.
477627f7eb2Smrg     ExceptionHeader *eh = ExceptionHeader.create(object);
478627f7eb2Smrg 
479627f7eb2Smrg     // Add to thrown exception stack.
480627f7eb2Smrg     eh.push();
481627f7eb2Smrg 
482627f7eb2Smrg     // Called by unwinder when exception object needs destruction by other than our code.
483627f7eb2Smrg     extern(C) void exception_cleanup(_Unwind_Reason_Code code, _Unwind_Exception* exc)
484627f7eb2Smrg     {
485627f7eb2Smrg         // If we haven't been caught by a foreign handler, then this is
486627f7eb2Smrg         // some sort of unwind error.  In that case just die immediately.
487627f7eb2Smrg         // _Unwind_DeleteException in the HP-UX IA64 libunwind library
488627f7eb2Smrg         //  returns _URC_NO_REASON and not _URC_FOREIGN_EXCEPTION_CAUGHT
489627f7eb2Smrg         // like the GCC _Unwind_DeleteException function does.
490627f7eb2Smrg         if (code != _URC_FOREIGN_EXCEPTION_CAUGHT && code != _URC_NO_REASON)
491627f7eb2Smrg             terminate("uncaught exception", __LINE__);
492627f7eb2Smrg 
493627f7eb2Smrg         auto eh = ExceptionHeader.toExceptionHeader(exc);
494627f7eb2Smrg         ExceptionHeader.free(eh);
495627f7eb2Smrg     }
496627f7eb2Smrg 
497627f7eb2Smrg     eh.unwindHeader.exception_cleanup = &exception_cleanup;
498627f7eb2Smrg 
499627f7eb2Smrg     // Runtime now expects us to do this first before unwinding.
500627f7eb2Smrg     _d_createTrace(eh.object, null);
501627f7eb2Smrg 
502627f7eb2Smrg     // We're happy with setjmp/longjmp exceptions or region-based
503627f7eb2Smrg     // exception handlers: entry points are provided here for both.
504627f7eb2Smrg     _Unwind_Reason_Code r = void;
505627f7eb2Smrg 
506627f7eb2Smrg     version (GNU_SjLj_Exceptions)
507627f7eb2Smrg         r = _Unwind_SjLj_RaiseException(&eh.unwindHeader);
508627f7eb2Smrg     else
509627f7eb2Smrg         r = _Unwind_RaiseException(&eh.unwindHeader);
510627f7eb2Smrg 
511627f7eb2Smrg     // If code == _URC_END_OF_STACK, then we reached top of stack without finding
512627f7eb2Smrg     // a handler for the exception.  Since each thread is run in a try/catch,
513627f7eb2Smrg     // this oughtn't happen.  If code is something else, we encountered some sort
514627f7eb2Smrg     // of heinous lossage from which we could not recover.  As is the way of such
515627f7eb2Smrg     // things, almost certainly we will have crashed before now, rather than
516627f7eb2Smrg     // actually being able to diagnose the problem.
517627f7eb2Smrg     if (r == _URC_END_OF_STACK)
518627f7eb2Smrg         terminate("uncaught exception", __LINE__);
519627f7eb2Smrg 
520627f7eb2Smrg     terminate("unwind error", __LINE__);
521627f7eb2Smrg }
522627f7eb2Smrg 
523627f7eb2Smrg static if (GNU_ARM_EABI_Unwinder)
524627f7eb2Smrg {
525627f7eb2Smrg     enum personality_fn_attributes = attribute("target", ("general-regs-only"));
526627f7eb2Smrg }
527627f7eb2Smrg else
528627f7eb2Smrg {
529627f7eb2Smrg     enum personality_fn_attributes = "";
530627f7eb2Smrg }
531627f7eb2Smrg 
532627f7eb2Smrg /**
533627f7eb2Smrg  * Read and extract information from the LSDA (.gcc_except_table section).
534627f7eb2Smrg  */
535627f7eb2Smrg @personality_fn_attributes
536627f7eb2Smrg _Unwind_Reason_Code scanLSDA(const(ubyte)* lsda, _Unwind_Exception_Class exceptionClass,
537627f7eb2Smrg                              _Unwind_Action actions, _Unwind_Exception* unwindHeader,
538627f7eb2Smrg                              _Unwind_Context* context, _Unwind_Word cfa,
539627f7eb2Smrg                              out _Unwind_Ptr landingPad, out int handler)
540627f7eb2Smrg {
541627f7eb2Smrg     // If no LSDA, then there are no handlers or cleanups.
542627f7eb2Smrg     if (lsda is null)
543627f7eb2Smrg         return CONTINUE_UNWINDING(unwindHeader, context);
544627f7eb2Smrg 
545627f7eb2Smrg     // Parse the LSDA header
546627f7eb2Smrg     auto p = lsda;
547627f7eb2Smrg 
548627f7eb2Smrg     auto Start = (context ? _Unwind_GetRegionStart(context) : 0);
549627f7eb2Smrg 
550627f7eb2Smrg     // Find @LPStart, the base to which landing pad offsets are relative.
551627f7eb2Smrg     ubyte LPStartEncoding = *p++;
552627f7eb2Smrg     _Unwind_Ptr LPStart = 0;
553627f7eb2Smrg 
554627f7eb2Smrg     if (LPStartEncoding != DW_EH_PE_omit)
555627f7eb2Smrg         LPStart = read_encoded_value(context, LPStartEncoding, &p);
556627f7eb2Smrg     else
557627f7eb2Smrg         LPStart = Start;
558627f7eb2Smrg 
559627f7eb2Smrg     // Find @TType, the base of the handler and exception spec type data.
560627f7eb2Smrg     ubyte TTypeEncoding = *p++;
561627f7eb2Smrg     const(ubyte)* TType = null;
562627f7eb2Smrg 
563627f7eb2Smrg     if (TTypeEncoding != DW_EH_PE_omit)
564627f7eb2Smrg     {
565627f7eb2Smrg         static if (__traits(compiles, _TTYPE_ENCODING))
566627f7eb2Smrg         {
567627f7eb2Smrg             // Older ARM EABI toolchains set this value incorrectly, so use a
568627f7eb2Smrg             // hardcoded OS-specific format.
569627f7eb2Smrg             TTypeEncoding = _TTYPE_ENCODING;
570627f7eb2Smrg         }
571627f7eb2Smrg         auto TTbase = read_uleb128(&p);
572627f7eb2Smrg         TType = p + TTbase;
573627f7eb2Smrg     }
574627f7eb2Smrg 
575627f7eb2Smrg     // The encoding and length of the call-site table; the action table
576627f7eb2Smrg     // immediately follows.
577627f7eb2Smrg     ubyte CSEncoding = *p++;
578627f7eb2Smrg     auto CSTableSize = read_uleb128(&p);
579627f7eb2Smrg     const(ubyte)* actionTable = p + CSTableSize;
580627f7eb2Smrg 
581627f7eb2Smrg     auto TTypeBase = base_of_encoded_value(TTypeEncoding, context);
582627f7eb2Smrg 
583627f7eb2Smrg     // Get instruction pointer (ip) at start of instruction that threw.
584627f7eb2Smrg     version (CRuntime_Glibc)
585627f7eb2Smrg     {
586627f7eb2Smrg         int ip_before_insn;
587627f7eb2Smrg         auto ip = _Unwind_GetIPInfo(context, &ip_before_insn);
588627f7eb2Smrg         if (!ip_before_insn)
589627f7eb2Smrg             --ip;
590627f7eb2Smrg     }
591627f7eb2Smrg     else
592627f7eb2Smrg     {
593627f7eb2Smrg         auto ip = _Unwind_GetIP(context);
594627f7eb2Smrg         --ip;
595627f7eb2Smrg     }
596627f7eb2Smrg 
597627f7eb2Smrg     bool saw_cleanup = false;
598627f7eb2Smrg     bool saw_handler = false;
599627f7eb2Smrg     const(ubyte)* actionRecord = null;
600627f7eb2Smrg 
601627f7eb2Smrg     version (GNU_SjLj_Exceptions)
602627f7eb2Smrg     {
603627f7eb2Smrg         // The given "IP" is an index into the call-site table, with two
604627f7eb2Smrg         // exceptions -- -1 means no-action, and 0 means terminate.
605627f7eb2Smrg         // But since we're using uleb128 values, we've not got random
606627f7eb2Smrg         // access to the array.
607627f7eb2Smrg         if (cast(int) ip <= 0)
608627f7eb2Smrg         {
609627f7eb2Smrg             return _URC_CONTINUE_UNWIND;
610627f7eb2Smrg         }
611627f7eb2Smrg         else
612627f7eb2Smrg         {
613627f7eb2Smrg             _uleb128_t CSLandingPad, CSAction;
614627f7eb2Smrg             do
615627f7eb2Smrg             {
616627f7eb2Smrg                 CSLandingPad = read_uleb128(&p);
617627f7eb2Smrg                 CSAction = read_uleb128(&p);
618627f7eb2Smrg             }
619627f7eb2Smrg             while (--ip);
620627f7eb2Smrg 
621627f7eb2Smrg             // Can never have null landing pad for sjlj -- that would have
622627f7eb2Smrg             // been indicated by a -1 call site index.
623627f7eb2Smrg             landingPad = CSLandingPad + 1;
624627f7eb2Smrg             if (CSAction)
625627f7eb2Smrg                 actionRecord = actionTable + CSAction - 1;
626627f7eb2Smrg         }
627627f7eb2Smrg     }
628627f7eb2Smrg     else
629627f7eb2Smrg     {
630627f7eb2Smrg         // Search the call-site table for the action associated with this IP.
631627f7eb2Smrg         while (p < actionTable)
632627f7eb2Smrg         {
633627f7eb2Smrg             // Note that all call-site encodings are "absolute" displacements.
634627f7eb2Smrg             auto CSStart = read_encoded_value(null, CSEncoding, &p);
635627f7eb2Smrg             auto CSLen = read_encoded_value(null, CSEncoding, &p);
636627f7eb2Smrg             auto CSLandingPad = read_encoded_value(null, CSEncoding, &p);
637627f7eb2Smrg             auto CSAction = read_uleb128(&p);
638627f7eb2Smrg 
639627f7eb2Smrg             // The table is sorted, so if we've passed the ip, stop.
640627f7eb2Smrg             if (ip < Start + CSStart)
641627f7eb2Smrg                 p = actionTable;
642627f7eb2Smrg             else if (ip < Start + CSStart + CSLen)
643627f7eb2Smrg             {
644627f7eb2Smrg                 if (CSLandingPad)
645627f7eb2Smrg                     landingPad = LPStart + CSLandingPad;
646627f7eb2Smrg                 if (CSAction)
647627f7eb2Smrg                     actionRecord = actionTable + CSAction - 1;
648627f7eb2Smrg                 break;
649627f7eb2Smrg             }
650627f7eb2Smrg         }
651627f7eb2Smrg     }
652627f7eb2Smrg 
653627f7eb2Smrg     if (landingPad == 0)
654627f7eb2Smrg     {
655627f7eb2Smrg         // IP is present, but has a null landing pad.
656627f7eb2Smrg         // No cleanups or handlers to be run.
657627f7eb2Smrg     }
658627f7eb2Smrg     else if (actionRecord is null)
659627f7eb2Smrg     {
660627f7eb2Smrg         // If ip is present, has a non-null landing pad, and a null
661627f7eb2Smrg         // action table offset, then there are only cleanups present.
662627f7eb2Smrg         // Cleanups use a zero switch value, as set above.
663627f7eb2Smrg         saw_cleanup = true;
664627f7eb2Smrg     }
665627f7eb2Smrg     else
666627f7eb2Smrg     {
667627f7eb2Smrg         // Otherwise we have a catch handler or exception specification.
668627f7eb2Smrg         handler = actionTableLookup(actions, unwindHeader, actionRecord,
669627f7eb2Smrg                                     exceptionClass, TTypeBase,
670627f7eb2Smrg                                     TType, TTypeEncoding,
671627f7eb2Smrg                                     saw_handler, saw_cleanup);
672627f7eb2Smrg     }
673627f7eb2Smrg 
674627f7eb2Smrg     // IP is not in table.  No associated cleanups.
675627f7eb2Smrg     if (!saw_handler && !saw_cleanup)
676627f7eb2Smrg         return CONTINUE_UNWINDING(unwindHeader, context);
677627f7eb2Smrg 
678627f7eb2Smrg     if (actions & _UA_SEARCH_PHASE)
679627f7eb2Smrg     {
680627f7eb2Smrg         if (!saw_handler)
681627f7eb2Smrg             return CONTINUE_UNWINDING(unwindHeader, context);
682627f7eb2Smrg 
683627f7eb2Smrg         // For domestic exceptions, we cache data from phase 1 for phase 2.
684627f7eb2Smrg         if (isGdcExceptionClass(exceptionClass))
685627f7eb2Smrg             ExceptionHeader.save(unwindHeader, cfa, handler, lsda, landingPad);
686627f7eb2Smrg 
687627f7eb2Smrg         return _URC_HANDLER_FOUND;
688627f7eb2Smrg     }
689627f7eb2Smrg 
690627f7eb2Smrg     return 0;
691627f7eb2Smrg }
692627f7eb2Smrg 
693627f7eb2Smrg /**
694627f7eb2Smrg  * Look up and return the handler index of the classType in Action Table.
695627f7eb2Smrg  */
696627f7eb2Smrg int actionTableLookup(_Unwind_Action actions, _Unwind_Exception* unwindHeader,
697627f7eb2Smrg                       const(ubyte)* actionRecord, _Unwind_Exception_Class exceptionClass,
698627f7eb2Smrg                       _Unwind_Ptr TTypeBase, const(ubyte)* TType,
699627f7eb2Smrg                       ubyte TTypeEncoding,
700627f7eb2Smrg                       out bool saw_handler, out bool saw_cleanup)
701627f7eb2Smrg {
702627f7eb2Smrg     ClassInfo thrownType;
703627f7eb2Smrg     if (isGdcExceptionClass(exceptionClass))
704627f7eb2Smrg     {
705627f7eb2Smrg         thrownType = ExceptionHeader.getClassInfo(unwindHeader);
706627f7eb2Smrg     }
707627f7eb2Smrg 
708627f7eb2Smrg     while (1)
709627f7eb2Smrg     {
710627f7eb2Smrg         auto ap = actionRecord;
711627f7eb2Smrg         auto ARFilter = read_sleb128(&ap);
712627f7eb2Smrg         auto apn = ap;
713627f7eb2Smrg         auto ARDisp = read_sleb128(&ap);
714627f7eb2Smrg 
715627f7eb2Smrg         if (ARFilter == 0)
716627f7eb2Smrg         {
717627f7eb2Smrg             // Zero filter values are cleanups.
718627f7eb2Smrg             saw_cleanup = true;
719627f7eb2Smrg         }
720627f7eb2Smrg         else if (actions & _UA_FORCE_UNWIND)
721627f7eb2Smrg         {
722627f7eb2Smrg             // During forced unwinding, we only run cleanups.
723627f7eb2Smrg         }
724627f7eb2Smrg         else if (ARFilter > 0)
725627f7eb2Smrg         {
726627f7eb2Smrg             // Positive filter values are handlers.
727627f7eb2Smrg             auto encodedSize = size_of_encoded_value(TTypeEncoding);
728627f7eb2Smrg 
729627f7eb2Smrg             // ARFilter is the negative index from TType, which is where
730627f7eb2Smrg             // the ClassInfo is stored.
731627f7eb2Smrg             const(ubyte)* tp = TType - ARFilter * encodedSize;
732627f7eb2Smrg 
733627f7eb2Smrg             auto entry = read_encoded_value_with_base(TTypeEncoding, TTypeBase, &tp);
734627f7eb2Smrg             ClassInfo ci = cast(ClassInfo)cast(void*)(entry);
735627f7eb2Smrg 
736627f7eb2Smrg             // D does not have catch-all handlers, and so the following
737627f7eb2Smrg             // assumes that we will never handle a null value.
738627f7eb2Smrg             assert(ci !is null);
739627f7eb2Smrg 
740627f7eb2Smrg             if (ci.classinfo is __cpp_type_info_ptr.classinfo
741627f7eb2Smrg                 && isGxxExceptionClass(exceptionClass))
742627f7eb2Smrg             {
743627f7eb2Smrg                 // catchType is the catch clause type_info.
744627f7eb2Smrg                 auto catchType = cast(CxxTypeInfo)((cast(__cpp_type_info_ptr)cast(void*)ci).ptr);
745627f7eb2Smrg                 auto thrownPtr = CxaExceptionHeader.getAdjustedPtr(unwindHeader, catchType);
746627f7eb2Smrg 
747627f7eb2Smrg                 if (thrownPtr !is null)
748627f7eb2Smrg                 {
749627f7eb2Smrg                     if (actions & _UA_SEARCH_PHASE)
750627f7eb2Smrg                         CxaExceptionHeader.save(unwindHeader, thrownPtr);
751627f7eb2Smrg                     saw_handler = true;
752627f7eb2Smrg                     return cast(int)ARFilter;
753627f7eb2Smrg                 }
754627f7eb2Smrg             }
755627f7eb2Smrg             else if (isGdcExceptionClass(exceptionClass)
756627f7eb2Smrg                      && _d_isbaseof(thrownType, ci))
757627f7eb2Smrg             {
758627f7eb2Smrg                 saw_handler = true;
759627f7eb2Smrg                 return cast(int)ARFilter;
760627f7eb2Smrg             }
761627f7eb2Smrg             else
762627f7eb2Smrg             {
763627f7eb2Smrg                 // ??? What to do about other GNU language exceptions.
764627f7eb2Smrg             }
765627f7eb2Smrg         }
766627f7eb2Smrg         else
767627f7eb2Smrg         {
768627f7eb2Smrg             // Negative filter values are exception specifications,
769627f7eb2Smrg             // which D does not use.
770627f7eb2Smrg             break;
771627f7eb2Smrg         }
772627f7eb2Smrg 
773627f7eb2Smrg         if (ARDisp == 0)
774627f7eb2Smrg             break;
775627f7eb2Smrg         actionRecord = apn + ARDisp;
776627f7eb2Smrg     }
777627f7eb2Smrg 
778627f7eb2Smrg     return 0;
779627f7eb2Smrg }
780627f7eb2Smrg 
781627f7eb2Smrg /**
782627f7eb2Smrg  * Called when the personality function has found neither a cleanup or handler.
783627f7eb2Smrg  * To support ARM EABI personality routines, that must also unwind the stack.
784627f7eb2Smrg  */
785627f7eb2Smrg @personality_fn_attributes
786627f7eb2Smrg _Unwind_Reason_Code CONTINUE_UNWINDING(_Unwind_Exception* unwindHeader, _Unwind_Context* context)
787627f7eb2Smrg {
788627f7eb2Smrg     static if (GNU_ARM_EABI_Unwinder)
789627f7eb2Smrg     {
790627f7eb2Smrg         if (__gnu_unwind_frame(unwindHeader, context) != _URC_OK)
791627f7eb2Smrg             return _URC_FAILURE;
792627f7eb2Smrg     }
793627f7eb2Smrg     return _URC_CONTINUE_UNWIND;
794627f7eb2Smrg }
795627f7eb2Smrg 
796627f7eb2Smrg /**
797627f7eb2Smrg  * Using a different personality function name causes link failures
798627f7eb2Smrg  * when trying to mix code using different exception handling models.
799627f7eb2Smrg  */
800627f7eb2Smrg version (GNU_SEH_Exceptions)
801627f7eb2Smrg {
802627f7eb2Smrg     enum PERSONALITY_FUNCTION = "__gdc_personality_imp";
803627f7eb2Smrg 
804627f7eb2Smrg     extern(C) EXCEPTION_DISPOSITION __gdc_personality_seh0(void* ms_exc, void* this_frame,
805627f7eb2Smrg                                                            void* ms_orig_context, void* ms_disp)
806627f7eb2Smrg     {
807627f7eb2Smrg         return _GCC_specific_handler(ms_exc, this_frame, ms_orig_context,
808627f7eb2Smrg                                      ms_disp, &__gdc_personality_imp);
809627f7eb2Smrg     }
810627f7eb2Smrg }
811627f7eb2Smrg else version (GNU_SjLj_Exceptions)
812627f7eb2Smrg {
813627f7eb2Smrg     enum PERSONALITY_FUNCTION = "__gdc_personality_sj0";
814627f7eb2Smrg 
815627f7eb2Smrg     private int __builtin_eh_return_data_regno(int x) { return x; }
816627f7eb2Smrg }
817627f7eb2Smrg else
818627f7eb2Smrg {
819627f7eb2Smrg     enum PERSONALITY_FUNCTION = "__gdc_personality_v0";
820627f7eb2Smrg }
821627f7eb2Smrg 
822627f7eb2Smrg /**
823627f7eb2Smrg  * The "personality" function, specific to each language.
824627f7eb2Smrg  */
825627f7eb2Smrg static if (GNU_ARM_EABI_Unwinder)
826627f7eb2Smrg {
827627f7eb2Smrg     pragma(mangle, PERSONALITY_FUNCTION)
828627f7eb2Smrg     @personality_fn_attributes
829627f7eb2Smrg     extern(C) _Unwind_Reason_Code gdc_personality(_Unwind_State state,
830627f7eb2Smrg                                                   _Unwind_Exception* unwindHeader,
831627f7eb2Smrg                                                   _Unwind_Context* context)
832627f7eb2Smrg     {
833627f7eb2Smrg         _Unwind_Action actions;
834627f7eb2Smrg 
835627f7eb2Smrg         switch (state & _US_ACTION_MASK)
836627f7eb2Smrg         {
837627f7eb2Smrg             case _US_VIRTUAL_UNWIND_FRAME:
838627f7eb2Smrg                 // If the unwind state pattern is (_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND)
839627f7eb2Smrg                 // then we don't need to search for any handler as it is not a real exception.
840627f7eb2Smrg                 // Just unwind the stack.
841627f7eb2Smrg                 if (state & _US_FORCE_UNWIND)
842627f7eb2Smrg                     return CONTINUE_UNWINDING(unwindHeader, context);
843627f7eb2Smrg                 actions = _UA_SEARCH_PHASE;
844627f7eb2Smrg                 break;
845627f7eb2Smrg 
846627f7eb2Smrg             case _US_UNWIND_FRAME_STARTING:
847627f7eb2Smrg                 actions = _UA_CLEANUP_PHASE;
848627f7eb2Smrg                 if (!(state & _US_FORCE_UNWIND)
849627f7eb2Smrg                     && unwindHeader.barrier_cache.sp == _Unwind_GetGR(context, UNWIND_STACK_REG))
850627f7eb2Smrg                     actions |= _UA_HANDLER_FRAME;
851627f7eb2Smrg                 break;
852627f7eb2Smrg 
853627f7eb2Smrg             case _US_UNWIND_FRAME_RESUME:
854627f7eb2Smrg                 return CONTINUE_UNWINDING(unwindHeader, context);
855627f7eb2Smrg 
856627f7eb2Smrg             default:
857627f7eb2Smrg                 terminate("unwind error", __LINE__);
858627f7eb2Smrg         }
859627f7eb2Smrg         actions |= state & _US_FORCE_UNWIND;
860627f7eb2Smrg 
861627f7eb2Smrg         // The dwarf unwinder assumes the context structure holds things like
862627f7eb2Smrg         // the function and LSDA pointers.  The ARM implementation caches these
863627f7eb2Smrg         // in the exception header (UCB).  To avoid rewriting everything we make
864627f7eb2Smrg         // the virtual IP register point at the UCB.
865627f7eb2Smrg         _Unwind_SetGR(context, UNWIND_POINTER_REG, cast(_Unwind_Ptr)unwindHeader);
866627f7eb2Smrg 
867627f7eb2Smrg         return __gdc_personality(actions, unwindHeader.exception_class,
868627f7eb2Smrg                                  unwindHeader, context);
869627f7eb2Smrg     }
870627f7eb2Smrg }
871627f7eb2Smrg else
872627f7eb2Smrg {
873627f7eb2Smrg     pragma(mangle, PERSONALITY_FUNCTION)
874627f7eb2Smrg     extern(C) _Unwind_Reason_Code gdc_personality(int iversion,
875627f7eb2Smrg                                                   _Unwind_Action actions,
876627f7eb2Smrg                                                   _Unwind_Exception_Class exceptionClass,
877627f7eb2Smrg                                                   _Unwind_Exception* unwindHeader,
878627f7eb2Smrg                                                   _Unwind_Context* context)
879627f7eb2Smrg     {
880627f7eb2Smrg         // Interface version check.
881627f7eb2Smrg         if (iversion != 1)
882627f7eb2Smrg             return _URC_FATAL_PHASE1_ERROR;
883627f7eb2Smrg 
884627f7eb2Smrg         return __gdc_personality(actions, exceptionClass, unwindHeader, context);
885627f7eb2Smrg     }
886627f7eb2Smrg }
887627f7eb2Smrg 
888627f7eb2Smrg @personality_fn_attributes
889627f7eb2Smrg private _Unwind_Reason_Code __gdc_personality(_Unwind_Action actions,
890627f7eb2Smrg                                               _Unwind_Exception_Class exceptionClass,
891627f7eb2Smrg                                               _Unwind_Exception* unwindHeader,
892627f7eb2Smrg                                               _Unwind_Context* context)
893627f7eb2Smrg {
894627f7eb2Smrg     const(ubyte)* lsda;
895627f7eb2Smrg     _Unwind_Ptr landingPad;
896627f7eb2Smrg     _Unwind_Word cfa;
897627f7eb2Smrg     int handler;
898627f7eb2Smrg 
899627f7eb2Smrg     // Shortcut for phase 2 found handler for domestic exception.
900627f7eb2Smrg     if (actions == (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME)
901627f7eb2Smrg         && isGdcExceptionClass(exceptionClass))
902627f7eb2Smrg     {
903627f7eb2Smrg         ExceptionHeader.restore(unwindHeader, handler, lsda, landingPad, cfa);
904627f7eb2Smrg         // Shouldn't have cached a null landing pad in phase 1.
905627f7eb2Smrg         if (landingPad == 0)
906627f7eb2Smrg             terminate("unwind error", __LINE__);
907627f7eb2Smrg     }
908627f7eb2Smrg     else
909627f7eb2Smrg     {
910627f7eb2Smrg         lsda = cast(ubyte*)_Unwind_GetLanguageSpecificData(context);
911627f7eb2Smrg 
912627f7eb2Smrg         static if (GNU_ARM_EABI_Unwinder)
913627f7eb2Smrg             cfa = _Unwind_GetGR(context, UNWIND_STACK_REG);
914627f7eb2Smrg         else
915627f7eb2Smrg             cfa = _Unwind_GetCFA(context);
916627f7eb2Smrg 
917627f7eb2Smrg         auto result = scanLSDA(lsda, exceptionClass, actions, unwindHeader,
918627f7eb2Smrg                                context, cfa, landingPad, handler);
919627f7eb2Smrg 
920627f7eb2Smrg         // Positive on handler found in phase 1, continue unwinding, or failure.
921627f7eb2Smrg         if (result)
922627f7eb2Smrg             return result;
923627f7eb2Smrg     }
924627f7eb2Smrg 
925627f7eb2Smrg     // Unexpected negative handler, call terminate directly.
926627f7eb2Smrg     if (handler < 0)
927627f7eb2Smrg         terminate("unwind error", __LINE__);
928627f7eb2Smrg 
929627f7eb2Smrg     // We can't use any of the deh routines with foreign exceptions,
930627f7eb2Smrg     // because they all expect unwindHeader to be an ExceptionHeader.
931627f7eb2Smrg     if (isGdcExceptionClass(exceptionClass))
932627f7eb2Smrg     {
933627f7eb2Smrg         // If there are any in-flight exceptions being thrown, chain our
934627f7eb2Smrg         // current object onto the end of the prevous object.
935627f7eb2Smrg         ExceptionHeader* eh = ExceptionHeader.toExceptionHeader(unwindHeader);
936627f7eb2Smrg         auto currentLsd = lsda;
937627f7eb2Smrg         auto currentCfa = cfa;
938627f7eb2Smrg         bool bypassed = false;
939627f7eb2Smrg 
940627f7eb2Smrg         while (eh.next)
941627f7eb2Smrg         {
942627f7eb2Smrg             ExceptionHeader* ehn = eh.next;
943627f7eb2Smrg             const(ubyte)* nextLsd;
944627f7eb2Smrg             _Unwind_Ptr nextLandingPad;
945627f7eb2Smrg             _Unwind_Word nextCfa;
946627f7eb2Smrg             int nextHandler;
947627f7eb2Smrg 
948627f7eb2Smrg             ExceptionHeader.restore(&ehn.unwindHeader, nextHandler, nextLsd, nextLandingPad, nextCfa);
949627f7eb2Smrg 
950627f7eb2Smrg             Error e = cast(Error)eh.object;
951627f7eb2Smrg             if (e !is null && !cast(Error)ehn.object)
952627f7eb2Smrg             {
953627f7eb2Smrg                 // We found an Error, bypass the exception chain.
954627f7eb2Smrg                 currentLsd = nextLsd;
955627f7eb2Smrg                 currentCfa = nextCfa;
956627f7eb2Smrg                 eh = ehn;
957627f7eb2Smrg                 bypassed = true;
958627f7eb2Smrg                 continue;
959627f7eb2Smrg             }
960627f7eb2Smrg 
961627f7eb2Smrg             // Don't combine when the exceptions are from different functions.
962627f7eb2Smrg             if (currentLsd != nextLsd && currentCfa != nextCfa)
963627f7eb2Smrg                 break;
964627f7eb2Smrg 
965627f7eb2Smrg             // Add our object onto the end of the existing chain.
966627f7eb2Smrg             Throwable n = ehn.object;
967627f7eb2Smrg             while (n.next)
968627f7eb2Smrg                 n = n.next;
969627f7eb2Smrg             n.next = eh.object;
970627f7eb2Smrg 
971627f7eb2Smrg             // Replace our exception object with in-flight one
972627f7eb2Smrg             eh.object = ehn.object;
973627f7eb2Smrg             if (nextHandler != handler && !bypassed)
974627f7eb2Smrg             {
975627f7eb2Smrg                 handler = nextHandler;
976627f7eb2Smrg                 ExceptionHeader.save(unwindHeader, cfa, handler, lsda, landingPad);
977627f7eb2Smrg             }
978627f7eb2Smrg 
979627f7eb2Smrg             // Exceptions chained, can now throw away the previous header.
980627f7eb2Smrg             eh.next = ehn.next;
981627f7eb2Smrg             _Unwind_DeleteException(&ehn.unwindHeader);
982627f7eb2Smrg         }
983627f7eb2Smrg 
984627f7eb2Smrg         if (bypassed)
985627f7eb2Smrg         {
986627f7eb2Smrg             eh = ExceptionHeader.toExceptionHeader(unwindHeader);
987627f7eb2Smrg             Error e = cast(Error)eh.object;
988627f7eb2Smrg             auto ehn = eh.next;
989627f7eb2Smrg             e.bypassedException = ehn.object;
990627f7eb2Smrg             eh.next = ehn.next;
991627f7eb2Smrg             _Unwind_DeleteException(&ehn.unwindHeader);
992627f7eb2Smrg         }
993627f7eb2Smrg     }
994627f7eb2Smrg 
995627f7eb2Smrg     // Set up registers and jump to cleanup or handler.
996627f7eb2Smrg     // For targets with pointers smaller than the word size, we must extend the
997627f7eb2Smrg     // pointer, and this extension is target dependent.
998627f7eb2Smrg     _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
999627f7eb2Smrg                   cast(_Unwind_Ptr)unwindHeader);
1000627f7eb2Smrg     _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), handler);
1001627f7eb2Smrg     _Unwind_SetIP(context, landingPad);
1002627f7eb2Smrg 
1003627f7eb2Smrg     return _URC_INSTALL_CONTEXT;
1004627f7eb2Smrg }
1005