Home | History | Annotate | Line # | Download | only in gcc
deh.d revision 1.1.1.1
      1 // GNU D Compiler exception personality routines.
      2 // Copyright (C) 2011-2019 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