Implement ARM EHABI exception handling.

This commit implements the ARM zero-cost exception handling
support for libc++abi.


git-svn-id: https://llvm.org/svn/llvm-project/libcxxabi/trunk@208466 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/cxxabi.h b/include/cxxabi.h
index 66ef6d4..82a7e74 100644
--- a/include/cxxabi.h
+++ b/include/cxxabi.h
@@ -44,6 +44,10 @@
 extern void * __cxa_get_exception_ptr(void * exceptionObject) throw();
 extern void * __cxa_begin_catch(void * exceptionObject) throw();
 extern void __cxa_end_catch();
+#if __arm__
+extern bool __cxa_begin_cleanup(void * exceptionObject) throw();
+extern void __cxa_end_cleanup();
+#endif
 extern std::type_info * __cxa_current_exception_type();
 
 // 2.5.4 Rethrowing Exceptions
diff --git a/include/unwind.h b/include/unwind.h
index 673e060..131657d 100644
--- a/include/unwind.h
+++ b/include/unwind.h
@@ -23,8 +23,16 @@
 #define LIBUNWIND_UNAVAIL
 #endif
 
+#if !defined(__USING_SJLJ_EXCEPTIONS__) && defined(__arm__) && \
+    !defined(__ARM_DWARF_EH__) && !defined(__APPLE__)
+#define LIBCXXABI_ARM_EHABI 1
+#else
+#define LIBCXXABI_ARM_EHABI 0
+#endif
+
 typedef enum {
   _URC_NO_REASON = 0,
+  _URC_OK = 0,
   _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
   _URC_FATAL_PHASE2_ERROR = 2,
   _URC_FATAL_PHASE1_ERROR = 3,
@@ -32,7 +40,8 @@
   _URC_END_OF_STACK = 5,
   _URC_HANDLER_FOUND = 6,
   _URC_INSTALL_CONTEXT = 7,
-  _URC_CONTINUE_UNWIND = 8
+  _URC_CONTINUE_UNWIND = 8,
+  _URC_FAILURE = 9
 } _Unwind_Reason_Code;
 
 typedef enum {
@@ -43,13 +52,69 @@
   _UA_END_OF_STACK = 16 // gcc extension to C++ ABI
 } _Unwind_Action;
 
+
+#if LIBCXXABI_ARM_EHABI
+typedef uint32_t _Unwind_State;
+
+static const _Unwind_State _US_VIRTUAL_UNWIND_FRAME   = 0;
+static const _Unwind_State _US_UNWIND_FRAME_STARTING  = 1;
+static const _Unwind_State _US_UNWIND_FRAME_RESUME    = 2;
+
+typedef uint32_t _Unwind_EHT_Header;
+
+struct _Unwind_Control_Block;
+typedef struct _Unwind_Control_Block _Unwind_Control_Block;
+typedef struct _Unwind_Control_Block _Unwind_Exception; /* Alias */
+
+struct _Unwind_Control_Block {
+  uint64_t exception_class;
+  void (*exception_cleanup)(_Unwind_Reason_Code, _Unwind_Control_Block*);
+
+  struct {
+    uint32_t reserved1;
+    uint32_t reserved2;
+    uint32_t reserved3;
+    uint32_t reserved4;
+    uint32_t reserved5;
+  } unwinder_cache;
+
+  struct {
+    uint32_t sp;
+    uint32_t bitpattern[5];
+  } barrier_cache;
+
+  struct {
+    uint32_t bitpattern[4];
+  } cleanup_cache;
+
+  struct {
+    uint32_t fnstart;
+    _Unwind_EHT_Header* ehtp;
+    uint32_t additional;
+    uint32_t reserved1;
+  } pr_cache;
+
+  long long int :0; /* Enforce the 8-byte alignment */
+};
+
+typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)
+      (_Unwind_State state,
+       _Unwind_Exception* exceptionObject,
+       struct _Unwind_Context* context);
+
+typedef _Unwind_Reason_Code (*__personality_routine)
+      (_Unwind_State state,
+       _Unwind_Exception* exceptionObject,
+       struct _Unwind_Context* context);
+#else
 struct _Unwind_Context;   // opaque
 struct _Unwind_Exception; // forward declaration
+typedef struct _Unwind_Exception _Unwind_Exception;
 
 struct _Unwind_Exception {
   uint64_t exception_class;
   void (*exception_cleanup)(_Unwind_Reason_Code reason,
-                            struct _Unwind_Exception *exc);
+                            _Unwind_Exception *exc);
   uintptr_t private_1; // non-zero means forced unwind
   uintptr_t private_2; // holds sp that phase1 found for phase2 to use
 #if !__LP64__
@@ -65,7 +130,7 @@
     (int version,
      _Unwind_Action actions,
      uint64_t exceptionClass,
-     struct _Unwind_Exception* exceptionObject,
+     _Unwind_Exception* exceptionObject,
      struct _Unwind_Context* context,
      void* stop_parameter );
 
@@ -73,8 +138,9 @@
       (int version,
        _Unwind_Action actions,
        uint64_t exceptionClass,
-       struct _Unwind_Exception* exceptionObject,
+       _Unwind_Exception* exceptionObject,
        struct _Unwind_Context* context);
+#endif
 
 #ifdef __cplusplus
 extern "C" {
@@ -85,29 +151,90 @@
 //
 #if __USING_SJLJ_EXCEPTIONS__
 extern _Unwind_Reason_Code
-    _Unwind_SjLj_RaiseException(struct _Unwind_Exception *exception_object);
-extern void _Unwind_SjLj_Resume(struct _Unwind_Exception *exception_object);
+    _Unwind_SjLj_RaiseException(_Unwind_Exception *exception_object);
+extern void _Unwind_SjLj_Resume(_Unwind_Exception *exception_object);
 #else
 extern _Unwind_Reason_Code
-    _Unwind_RaiseException(struct _Unwind_Exception *exception_object);
-extern void _Unwind_Resume(struct _Unwind_Exception *exception_object);
+    _Unwind_RaiseException(_Unwind_Exception *exception_object);
+extern void _Unwind_Resume(_Unwind_Exception *exception_object);
 #endif
-extern void _Unwind_DeleteException(struct _Unwind_Exception *exception_object);
+extern void _Unwind_DeleteException(_Unwind_Exception *exception_object);
+
+#if LIBCXXABI_ARM_EHABI
+typedef enum {
+  _UVRSC_CORE = 0,
+  _UVRSC_VFP = 1,
+  _UVRSC_WMMXD = 3,
+  _UVRSC_WMMXC = 4
+} _Unwind_VRS_RegClass;
+
+typedef enum {
+  _UVRSD_UINT32 = 0,
+  _UVRSD_VFPX = 1,
+  _UVRSD_UINT64 = 3,
+  _UVRSD_FLOAT = 4,
+  _UVRSD_DOUBLE = 5
+} _Unwind_VRS_DataRepresentation;
+
+typedef enum {
+  _UVRSR_OK = 0,
+  _UVRSR_NOT_IMPLEMENTED = 1,
+  _UVRSR_FAILED = 2
+} _Unwind_VRS_Result;
+
+extern _Unwind_VRS_Result _Unwind_VRS_Get(_Unwind_Context* context,
+                                          _Unwind_VRS_RegClass regclass,
+                                          uint32_t regno,
+                                          _Unwind_VRS_DataRepresentation representation,
+                                          void *valuep);
+
+extern _Unwind_VRS_Result _Unwind_VRS_Set(_Unwind_Context* context,
+                                          _Unwind_VRS_RegClass regclass,
+                                          uint32_t regno,
+                                          _Unwind_VRS_DataRepresentation representation,
+                                          void *valuep);
+
+static inline uintptr_t _Unwind_GetGR(struct _Unwind_Context* context,
+                                      int index) {
+  uintptr_t value = 0;
+  _Unwind_VRS_Get(context, _UVRSC_CORE, (uint32_t)index, _UVRSD_UINT32, &value);
+  return value;
+}
+
+static inline void _Unwind_SetGR(struct _Unwind_Context* context, int index,
+                                 uintptr_t new_value) {
+  _Unwind_VRS_Set(context, _UVRSC_CORE, (uint32_t)index,
+                  _UVRSD_UINT32, &new_value);
+}
+
+static inline uintptr_t _Unwind_GetIP(struct _Unwind_Context* context) {
+  // remove the thumb-bit before returning
+  return (_Unwind_GetGR(context, 15) & (~(uintptr_t)0x1));
+}
+
+static inline void _Unwind_SetIP(struct _Unwind_Context* context,
+                                 uintptr_t new_value) {
+  uintptr_t thumb_bit = _Unwind_GetGR(context, 15) & ((uintptr_t)0x1);
+  _Unwind_SetGR(context, 15, new_value | thumb_bit);
+}
+#else
 extern uintptr_t _Unwind_GetGR(struct _Unwind_Context *context, int index);
 extern void _Unwind_SetGR(struct _Unwind_Context *context, int index,
                           uintptr_t new_value);
 extern uintptr_t _Unwind_GetIP(struct _Unwind_Context *context);
 extern void _Unwind_SetIP(struct _Unwind_Context *, uintptr_t new_value);
+#endif
+
 extern uintptr_t _Unwind_GetRegionStart(struct _Unwind_Context *context);
 extern uintptr_t
     _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context);
 #if __USING_SJLJ_EXCEPTIONS__
 extern _Unwind_Reason_Code
-    _Unwind_SjLj_ForcedUnwind(struct _Unwind_Exception *exception_object,
+    _Unwind_SjLj_ForcedUnwind(_Unwind_Exception *exception_object,
                               _Unwind_Stop_Fn stop, void *stop_parameter);
 #else
 extern _Unwind_Reason_Code
-    _Unwind_ForcedUnwind(struct _Unwind_Exception *exception_object,
+    _Unwind_ForcedUnwind(_Unwind_Exception *exception_object,
                          _Unwind_Stop_Fn stop, void *stop_parameter);
 #endif
 
@@ -126,10 +253,10 @@
 //
 #if __USING_SJLJ_EXCEPTIONS__
 extern _Unwind_Reason_Code
-    _Unwind_SjLj_Resume_or_Rethrow(struct _Unwind_Exception *exception_object);
+    _Unwind_SjLj_Resume_or_Rethrow(_Unwind_Exception *exception_object);
 #else
 extern _Unwind_Reason_Code
-    _Unwind_Resume_or_Rethrow(struct _Unwind_Exception *exception_object);
+    _Unwind_Resume_or_Rethrow(_Unwind_Exception *exception_object);
 #endif
 
 // _Unwind_Backtrace() is a gcc extension that walks the stack and calls the
diff --git a/src/cxa_demangle.cpp b/src/cxa_demangle.cpp
index d1661e8..2ad832a 100644
--- a/src/cxa_demangle.cpp
+++ b/src/cxa_demangle.cpp
@@ -155,7 +155,11 @@
 template <>
 struct float_data<long double>
 {
+#if defined(__arm__)
+    static const size_t mangled_size = 16;
+#else
     static const size_t mangled_size = 20;  // May need to be adjusted to 16 or 24 on other platforms
+#endif
     static const size_t max_demangled_size = 40;
     static constexpr const char* spec = "%LaL";
 };
diff --git a/src/cxa_exception.cpp b/src/cxa_exception.cpp
index 9bc6ad6..13c060c 100644
--- a/src/cxa_exception.cpp
+++ b/src/cxa_exception.cpp
@@ -256,11 +256,102 @@
 void*
 __cxa_get_exception_ptr(void* unwind_exception) throw()
 {
+#if LIBCXXABI_ARM_EHABI
+    return reinterpret_cast<void*>(
+           static_cast<_Unwind_Control_Block*>(unwind_exception)->barrier_cache.bitpattern[0]);
+#else
     return cxa_exception_from_exception_unwind_exception
            (
                static_cast<_Unwind_Exception*>(unwind_exception)
            )->adjustedPtr;
+#endif
 }
+
+#if LIBCXXABI_ARM_EHABI
+/*
+The routine to be called before the cleanup.  This will save __cxa_exception in
+__cxa_eh_globals, so that __cxa_end_cleanup() can recover later.
+*/
+bool
+__cxa_begin_cleanup(void* unwind_arg) throw ()
+{
+    _Unwind_Exception* unwind_exception = static_cast<_Unwind_Exception*>(unwind_arg);
+    __cxa_eh_globals* globals = __cxa_get_globals();
+    __cxa_exception* exception_header =
+        cxa_exception_from_exception_unwind_exception(unwind_exception);
+
+    if (isOurExceptionClass(unwind_exception))
+    {
+        if (0 == exception_header->propagationCount)
+        {
+            exception_header->nextPropagatingException = globals->propagatingExceptions;
+            globals->propagatingExceptions = exception_header;
+        }
+        ++exception_header->propagationCount;
+    }
+    else
+    {
+        // If the propagatingExceptions stack is not empty, since we can't
+        // chain the foreign exception, terminate it.
+        if (NULL != globals->propagatingExceptions)
+            std::terminate();
+        globals->propagatingExceptions = exception_header;
+    }
+    return true;
+}
+
+/*
+The routine to be called after the cleanup has been performed.  It will get the
+propagating __cxa_exception from __cxa_eh_globals, and continue the stack
+unwinding with _Unwind_Resume.
+
+According to ARM EHABI 8.4.1, __cxa_end_cleanup() should not clobber any
+register, thus we have to write this function in assembly so that we can save
+{r1, r2, r3}.  We don't have to save r0 because it is the return value and the
+first argument to _Unwind_Resume().  In addition, we are saving r4 in order to
+align the stack to 16 bytes, even though it is a callee-save register.
+*/
+__attribute__((used)) static _Unwind_Exception *
+__cxa_end_cleanup_impl()
+{
+    __cxa_eh_globals* globals = __cxa_get_globals();
+    __cxa_exception* exception_header = globals->propagatingExceptions;
+    if (NULL == exception_header)
+    {
+        // It seems that __cxa_begin_cleanup() is not called properly.
+        // We have no choice but terminate the program now.
+        std::terminate();
+    }
+
+    if (isOurExceptionClass(&exception_header->unwindHeader))
+    {
+        --exception_header->propagationCount;
+        if (0 == exception_header->propagationCount)
+        {
+            globals->propagatingExceptions = exception_header->nextPropagatingException;
+            exception_header->nextPropagatingException = NULL;
+        }
+    }
+    else
+    {
+        globals->propagatingExceptions = NULL;
+    }
+    return &exception_header->unwindHeader;
+}
+
+asm (
+    "	.pushsection	.text.__cxa_end_cleanup\n"
+    "	.globl	__cxa_end_cleanup\n"
+    "	.type	__cxa_end_cleanup,%function\n"
+    "__cxa_end_cleanup:\n"
+    "	push	{r1, r2, r3, r4}\n"
+    "	bl	__cxa_end_cleanup_impl\n"
+    "	pop	{r1, r2, r3, r4}\n"
+    "	bl	_Unwind_Resume\n"
+    "	bl	abort\n"
+    "	.popsection"
+);
+#endif  // LIBCXXABI_ARM_EHABI
     
 /*
 This routine can catch foreign or native exceptions.  If native, the exception
@@ -320,7 +411,11 @@
             globals->caughtExceptions = exception_header;
         }
         globals->uncaughtExceptions -= 1;   // Not atomically, since globals are thread-local
+#if LIBCXXABI_ARM_EHABI
+        return reinterpret_cast<void*>(exception_header->unwindHeader.barrier_cache.bitpattern[0]);
+#else
         return exception_header->adjustedPtr;
+#endif
     }
     // Else this is a foreign exception
     // If the caughtExceptions stack is not empty, terminate
diff --git a/src/cxa_exception.hpp b/src/cxa_exception.hpp
index 03b7a84..592dd50 100644
--- a/src/cxa_exception.hpp
+++ b/src/cxa_exception.hpp
@@ -27,7 +27,7 @@
 static const uint64_t get_vendor_and_language =     0xFFFFFFFFFFFFFF00; // mask for CLNGC++
                                                     
 struct __cxa_exception { 
-#if __LP64__
+#if __LP64__ || LIBCXXABI_ARM_EHABI
     // This is a new field to support C++ 0x exception_ptr.
     // For binary compatibility it is at the start of this
     // struct which is prepended to the object thrown in
@@ -45,7 +45,7 @@
 
     int handlerCount;
 
-#ifdef __ARM_EABI_UNWINDER__
+#if LIBCXXABI_ARM_EHABI
     __cxa_exception* nextPropagatingException;
     int propagationCount;
 #else
@@ -56,7 +56,7 @@
     void *adjustedPtr;
 #endif
 
-#if !__LP64__
+#if !__LP64__ && !LIBCXXABI_ARM_EHABI
     // This is a new field to support C++ 0x exception_ptr.
     // For binary compatibility it is placed where the compiler
     // previously adding padded to 64-bit align unwindHeader.
@@ -82,7 +82,7 @@
 
     int handlerCount;
     
-#ifdef __ARM_EABI_UNWINDER__
+#if LIBCXXABI_ARM_EHABI
     __cxa_exception* nextPropagatingException;
     int propagationCount;
 #else
@@ -103,7 +103,7 @@
 struct __cxa_eh_globals {
     __cxa_exception *   caughtExceptions;
     unsigned int        uncaughtExceptions;
-#ifdef __ARM_EABI_UNWINDER__
+#if LIBCXXABI_ARM_EHABI
     __cxa_exception* propagatingExceptions;
 #endif
 };
diff --git a/src/cxa_personality.cpp b/src/cxa_personality.cpp
index dfdcfbb..c177743 100644
--- a/src/cxa_personality.cpp
+++ b/src/cxa_personality.cpp
@@ -307,6 +307,33 @@
     std::terminate();
 }
 
+#if LIBCXXABI_ARM_EHABI
+static const void* read_target2_value(const void* ptr)
+{
+    uintptr_t offset = *reinterpret_cast<const uintptr_t*>(ptr);
+    if (!offset)
+        return 0;
+    return *reinterpret_cast<const void**>(reinterpret_cast<uintptr_t>(ptr) + offset);
+}
+
+static const __shim_type_info*
+get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo,
+                   uint8_t ttypeEncoding, bool native_exception,
+                   _Unwind_Exception* unwind_exception)
+{
+    if (classInfo == 0)
+    {
+        // this should not happen.  Indicates corrupted eh_table.
+        call_terminate(native_exception, unwind_exception);
+    }
+
+    assert(ttypeEncoding == DW_EH_PE_absptr && "Unexpected TTypeEncoding");
+    (void)ttypeEncoding;
+
+    const uint8_t* ttypePtr = classInfo - ttypeIndex * sizeof(uintptr_t);
+    return reinterpret_cast<const __shim_type_info*>(read_target2_value(ttypePtr));
+}
+#else
 static
 const __shim_type_info*
 get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo,
@@ -342,6 +369,7 @@
     classInfo -= ttypeIndex;
     return (const __shim_type_info*)readEncodedPointer(&classInfo, ttypeEncoding);
 }
+#endif
 
 /*
     This is checking a thrown exception type, excpType, against a possibly empty
@@ -352,6 +380,49 @@
     the list will catch a excpType.  If any catchType in the list can catch an
     excpType, then this exception spec does not catch the excpType.
 */
+#if LIBCXXABI_ARM_EHABI
+static
+bool
+exception_spec_can_catch(int64_t specIndex, const uint8_t* classInfo,
+                         uint8_t ttypeEncoding, const __shim_type_info* excpType,
+                         void* adjustedPtr, _Unwind_Exception* unwind_exception)
+{
+    if (classInfo == 0)
+    {
+        // this should not happen.   Indicates corrupted eh_table.
+        call_terminate(false, unwind_exception);
+    }
+
+    assert(ttypeEncoding == DW_EH_PE_absptr && "Unexpected TTypeEncoding");
+    (void)ttypeEncoding;
+
+    // specIndex is negative of 1-based byte offset into classInfo;
+    specIndex = -specIndex;
+    --specIndex;
+    const void** temp = reinterpret_cast<const void**>(
+        reinterpret_cast<uintptr_t>(classInfo) +
+        static_cast<uintptr_t>(specIndex) * sizeof(uintptr_t));
+    // If any type in the spec list can catch excpType, return false, else return true
+    //    adjustments to adjustedPtr are ignored.
+    while (true)
+    {
+        // ARM EHABI exception specification table (filter table) consists of
+        // several pointers which will directly point to the type info object
+        // (instead of ttypeIndex).  The table will be terminated with 0.
+        const void** ttypePtr = temp++;
+        if (*ttypePtr == 0)
+            break;
+        // We can get the __shim_type_info simply by performing a
+        // R_ARM_TARGET2 relocation, and cast the result to __shim_type_info.
+        const __shim_type_info* catchType =
+            static_cast<const __shim_type_info*>(read_target2_value(ttypePtr));
+        void* tempPtr = adjustedPtr;
+        if (catchType->can_catch(excpType, tempPtr))
+            return false;
+    }
+    return true;
+}
+#else
 static
 bool
 exception_spec_can_catch(int64_t specIndex, const uint8_t* classInfo,
@@ -385,6 +456,7 @@
     }
     return true;
 }
+#endif
 
 static
 void*
@@ -837,6 +909,7 @@
         Else a cleanup is not found: return _URC_CONTINUE_UNWIND
 */
 
+#if !LIBCXXABI_ARM_EHABI
 _Unwind_Reason_Code
 #if __USING_SJLJ_EXCEPTIONS__
 __gxx_personality_sj0
@@ -848,6 +921,7 @@
 {
     if (version != 1 || unwind_exception == 0 || context == 0)
         return _URC_FATAL_PHASE1_ERROR;
+
     bool native_exception = (exceptionClass     & get_vendor_and_language) ==
                             (kOurExceptionClass & get_vendor_and_language);
     scan_results results;
@@ -924,6 +998,133 @@
     // We were called improperly: neither a phase 1 or phase 2 search
     return _URC_FATAL_PHASE1_ERROR;
 }
+#else
+
+extern "C" _Unwind_Reason_Code __gnu_unwind_frame(_Unwind_Exception*, _Unwind_Context*);
+
+// Helper function to unwind one frame.
+// ARM EHABI 7.3 and 7.4: If the personality function returns _URC_CONTINUE_UNWIND, the
+// personality routine should update the virtual register set (VRS) according to the
+// corresponding frame unwinding instructions (ARM EHABI 9.3.)
+static _Unwind_Reason_Code continue_unwind(_Unwind_Exception* unwind_exception,
+                                           _Unwind_Context* context)
+{
+    if (__gnu_unwind_frame(unwind_exception, context) != _URC_OK)
+        return _URC_FAILURE;
+    return _URC_CONTINUE_UNWIND;
+}
+
+// ARM register names
+static const uint32_t REG_UCB = 12;  // Register to save _Unwind_Control_Block
+static const uint32_t REG_SP = 13;
+
+static void save_results_to_barrier_cache(_Unwind_Exception* unwind_exception,
+                                          const scan_results& results)
+{
+    unwind_exception->barrier_cache.bitpattern[0] = (uint32_t)results.adjustedPtr;
+    unwind_exception->barrier_cache.bitpattern[1] = (uint32_t)results.actionRecord;
+    unwind_exception->barrier_cache.bitpattern[2] = (uint32_t)results.languageSpecificData;
+    unwind_exception->barrier_cache.bitpattern[3] = (uint32_t)results.landingPad;
+    unwind_exception->barrier_cache.bitpattern[4] = (uint32_t)results.ttypeIndex;
+}
+
+static void load_results_from_barrier_cache(scan_results& results,
+                                            const _Unwind_Exception* unwind_exception)
+{
+    results.adjustedPtr = (void*)unwind_exception->barrier_cache.bitpattern[0];
+    results.actionRecord = (const uint8_t*)unwind_exception->barrier_cache.bitpattern[1];
+    results.languageSpecificData = (const uint8_t*)unwind_exception->barrier_cache.bitpattern[2];
+    results.landingPad = (uintptr_t)unwind_exception->barrier_cache.bitpattern[3];
+    results.ttypeIndex = (int64_t)(int32_t)unwind_exception->barrier_cache.bitpattern[4];
+}
+
+extern "C" _Unwind_Reason_Code
+__gxx_personality_v0(_Unwind_State state,
+                     _Unwind_Exception* unwind_exception,
+                     _Unwind_Context* context)
+{
+    if (unwind_exception == 0 || context == 0)
+        return _URC_FATAL_PHASE1_ERROR;
+
+    bool native_exception = (unwind_exception->exception_class & get_vendor_and_language) ==
+                            (kOurExceptionClass & get_vendor_and_language);
+
+    // Copy the address of _Unwind_Control_Block to r12 so that _Unwind_GetLangauageSpecificData()
+    // and _Unwind_GetRegionStart() can return correct address.
+    _Unwind_SetGR(context, REG_UCB, reinterpret_cast<uint32_t>(unwind_exception));
+
+    scan_results results;
+    switch (state) {
+    case _US_VIRTUAL_UNWIND_FRAME:
+        // Phase 1 search:  All we're looking for in phase 1 is a handler that halts unwinding
+        scan_eh_tab(results, _UA_SEARCH_PHASE, native_exception, unwind_exception, context);
+        if (results.reason == _URC_HANDLER_FOUND)
+        {
+            unwind_exception->barrier_cache.sp = _Unwind_GetGR(context, REG_SP);
+            if (native_exception)
+                save_results_to_barrier_cache(unwind_exception, results);
+            return _URC_HANDLER_FOUND;
+        }
+        // Did not find the catch handler
+        if (results.reason == _URC_CONTINUE_UNWIND)
+            return continue_unwind(unwind_exception, context);
+        return results.reason;
+
+    case _US_UNWIND_FRAME_STARTING:
+        // Phase 2 search
+        if (unwind_exception->barrier_cache.sp == _Unwind_GetGR(context, REG_SP))
+        {
+            // Found a catching handler in phase 1
+            if (native_exception)
+            {
+                // Load the result from the native exception barrier cache.
+                load_results_from_barrier_cache(results, unwind_exception);
+                results.reason = _URC_HANDLER_FOUND;
+            }
+            else
+            {
+                // Search for the catching handler again for the foreign exception.
+                scan_eh_tab(results, static_cast<_Unwind_Action>(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME),
+                            native_exception, unwind_exception, context);
+                if (results.reason != _URC_HANDLER_FOUND)  // phase1 search should guarantee to find one
+                    call_terminate(native_exception, unwind_exception);
+            }
+
+            // Install the context for the catching handler
+            set_registers(unwind_exception, context, results);
+            return _URC_INSTALL_CONTEXT;
+        }
+
+        // Search for a (non-catching) cleanup
+        scan_eh_tab(results, _UA_CLEANUP_PHASE, native_exception, unwind_exception, context);
+        if (results.reason == _URC_HANDLER_FOUND)
+        {
+            // Found a non-catching handler
+
+            // ARM EHABI 8.4.2: Before we can jump to the cleanup handler, we have to setup some
+            // internal data structures, so that __cxa_end_cleanup() can get unwind_exception from
+            // __cxa_get_globals().
+            __cxa_begin_cleanup(unwind_exception);
+
+            // Install the context for the cleanup handler
+            set_registers(unwind_exception, context, results);
+            return _URC_INSTALL_CONTEXT;
+        }
+
+        // Did not find any handler
+        if (results.reason == _URC_CONTINUE_UNWIND)
+            return continue_unwind(unwind_exception, context);
+        return results.reason;
+
+    case _US_UNWIND_FRAME_RESUME:
+        return continue_unwind(unwind_exception, context);
+    }
+
+    // We were called improperly: neither a phase 1 or phase 2 search
+    return _URC_FATAL_PHASE1_ERROR;
+}
+#endif
+
 
 __attribute__((noreturn))
 void
@@ -948,8 +1149,13 @@
         u_handler = old_exception_header->unexpectedHandler;
         // If std::__unexpected(u_handler) rethrows the same exception,
         //   these values get overwritten by the rethrow.  So save them now:
+#if LIBCXXABI_ARM_EHABI
+        ttypeIndex = (int64_t)(int32_t)unwind_exception->barrier_cache.bitpattern[4];
+        lsda = (const uint8_t*)unwind_exception->barrier_cache.bitpattern[2];
+#else
         ttypeIndex = old_exception_header->handlerSwitchValue;
         lsda = old_exception_header->languageSpecificData;
+#endif
     }
     else
     {
diff --git a/test/test_demangle.cpp b/test/test_demangle.cpp
index bb5b66d..e00c913 100644
--- a/test/test_demangle.cpp
+++ b/test/test_demangle.cpp
@@ -14,6 +14,9 @@
 #include <cassert>
 #include <chrono>
 
+// Is long double fp80?  (Only x87 extended double has 64-bit mantissa)
+#define LDBL_FP80 (__LDBL_MANT_DIG__ == 64)
+
 const char* cases[][2] =
 {
     {"_Z1A", "A"},
@@ -29513,7 +29516,9 @@
     {"_ZN5test01fIiEEvT_RAszcl3ovlcvS1__EE_c", "void test0::f<int>(int, char (&) [sizeof (ovl((int)()))])"},
     {"_ZN5test01gIfEEvRAszplcvT__ELf40a00000E_c", "void test0::g<float>(char (&) [sizeof (((float)()) + (0x1.4p+2f))])"},
     {"_ZN5test01hIfEEvRAszplcvT__ELd4014000000000000E_c", "void test0::h<float>(char (&) [sizeof (((float)()) + (0x1.4p+2))])"},
+#if LDBL_FP80
     {"_ZN5test01hIfEEvRAcvjplstT_Le4001a000000000000000E_c", "void test0::h<float>(char (&) [(unsigned int)((sizeof (float)) + (0xap-1L))])"},
+#endif
     {"_ZN5test01jINS_1AEEEvRAszdtcvT__E6buffer_c", "void test0::j<test0::A>(char (&) [sizeof ((test0::A)().buffer)])"},
     {"_ZN5test11fINS_1XEiEEvT_IT0_E", "void test1::f<test1::X, int>(test1::X<int>)"},
     {"_ZN5test211read_memberINS_1AEEEDtptcvPT_Li0E6memberERS2_", "decltype((test2::A*)(0)->member) test2::read_member<test2::A>(test2::A&)"},
@@ -29597,6 +29602,9 @@
     "NSoERj5E=Y1[uM:ga",
     "Aon_PmKVPDk7?fg4XP5smMUL6;<WsI_mgbf23cCgsHbT<l8EE\0uVRkNOoXDrgdA4[8IU>Vl<>IL8ayHpiVDDDXTY;^o9;i",
     "_ZNSt16allocator_traitsISaIN4llvm3sys2fs18directory_iteratorEEE9constructIS3_IS3_EEEDTcl12_S_constructfp_fp0_spcl7forwardIT0_Efp1_EEERS4_PT_DpOS7_",
+#if !LDBL_FP80
+    "_ZN5test01hIfEEvRAcvjplstT_Le4001a000000000000000E_c",
+#endif
 };
 
 const unsigned NI = sizeof(invalid_cases) / sizeof(invalid_cases[0]);