Index: libcxxabi/trunk/include/unwind.h =================================================================== --- libcxxabi/trunk/include/unwind.h +++ libcxxabi/trunk/include/unwind.h @@ -207,10 +207,6 @@ uint32_t discriminator, _Unwind_VRS_DataRepresentation representation); -extern _Unwind_Reason_Code _Unwind_VRS_Interpret(_Unwind_Context *context, - uint32_t *data, size_t offset, - size_t len); - static inline uintptr_t _Unwind_GetGR(struct _Unwind_Context* context, int index) { uintptr_t value = 0; Index: libcxxabi/trunk/src/Unwind/Unwind-EHABI.cpp =================================================================== --- libcxxabi/trunk/src/Unwind/Unwind-EHABI.cpp +++ libcxxabi/trunk/src/Unwind/Unwind-EHABI.cpp @@ -20,6 +20,7 @@ #include "config.h" #include "libunwind.h" +#include "libunwind_ext.h" #include "unwind.h" #include "../private_typeinfo.h" @@ -28,8 +29,8 @@ // Strange order: take words in order, but inside word, take from most to least // signinficant byte. -uint8_t getByte(uint32_t* data, size_t offset) { - uint8_t* byteData = reinterpret_cast(data); +uint8_t getByte(const uint32_t* data, size_t offset) { + const uint8_t* byteData = reinterpret_cast(data); return byteData[(offset & ~(size_t)0x03) + (3 - (offset & (size_t)0x03))]; } @@ -166,25 +167,15 @@ _Unwind_Control_Block* ucbp, struct _Unwind_Context* context) { // Read the compact model EHT entry's header # 6.3 - uint32_t* unwindingData = ucbp->pr_cache.ehtp; - uint32_t unwindInfo = *unwindingData; - assert((unwindInfo & 0xf0000000) == 0x80000000 && "Must be a compact entry"); + const uint32_t* unwindingData = ucbp->pr_cache.ehtp; + assert((*unwindingData & 0xf0000000) == 0x80000000 && "Must be a compact entry"); Descriptor::Format format = - static_cast((unwindInfo & 0x0f000000) >> 24); + static_cast((*unwindingData & 0x0f000000) >> 24); size_t len = 0; - size_t startOffset = 0; - switch (format) { - case Descriptor::SU16: - len = 4; - startOffset = 1; - break; - case Descriptor::LU16: - case Descriptor::LU32: - len = 4 + 4 * ((unwindInfo & 0x00ff0000) >> 16); - startOffset = 2; - break; - default: - return _URC_FAILURE; + size_t off = 0; + unwindingData = decode_eht_entry(unwindingData, &off, &len); + if (unwindingData == nullptr) { + return _URC_FAILURE; } // Handle descriptors before unwinding so they are processed in the context @@ -198,7 +189,7 @@ if (result != _URC_CONTINUE_UNWIND) return result; - return _Unwind_VRS_Interpret(context, unwindingData, startOffset, len); + return _Unwind_VRS_Interpret(context, unwindingData, off, len); } // Generates mask discriminator for _Unwind_VRS_Pop, e.g. for _UVRSC_CORE / @@ -215,9 +206,46 @@ } // end anonymous namespace +/** + * Decodes an EHT entry. + * + * @param data Pointer to EHT. + * @param[out] off Offset from return value (in bytes) to begin interpretation. + * @param[out] len Number of bytes in unwind code. + * @return Pointer to beginning of unwind code. + */ +extern "C" const uint32_t* +decode_eht_entry(const uint32_t* data, size_t* off, size_t* len) { + if ((*data & 0x80000000) == 0) { + // 6.2: Generic Model + *off = 1; // First byte is size data. + *len = (((data[1] >> 24) & 0xff) + 1) * 4; + data++; // Skip the first word, which is the prel31 offset. + } else { + // 6.3: ARM Compact Model + Descriptor::Format format = + static_cast((*data & 0x0f000000) >> 24); + switch (format) { + case Descriptor::SU16: + *len = 4; + *off = 1; + break; + case Descriptor::LU16: + case Descriptor::LU32: + *len = 4 + 4 * ((*data & 0x00ff0000) >> 16); + *off = 2; + break; + default: + return nullptr; + } + } + + return data; +} + _Unwind_Reason_Code _Unwind_VRS_Interpret( _Unwind_Context* context, - uint32_t* data, + const uint32_t* data, size_t offset, size_t len) { bool wrotePC = false; Index: libcxxabi/trunk/src/Unwind/UnwindLevel1-gcc-ext.c =================================================================== --- libcxxabi/trunk/src/Unwind/UnwindLevel1-gcc-ext.c +++ libcxxabi/trunk/src/Unwind/UnwindLevel1-gcc-ext.c @@ -109,6 +109,7 @@ // walk each frame while (true) { + _Unwind_Reason_Code result; // ask libuwind to get next frame (skip over first frame which is // _Unwind_Backtrace()) @@ -119,6 +120,28 @@ return _URC_END_OF_STACK; } +#if LIBCXXABI_ARM_EHABI + // Get the information for this frame. + unw_proc_info_t frameInfo; + if (unw_get_proc_info(&cursor, &frameInfo) != UNW_ESUCCESS) { + return _URC_END_OF_STACK; + } + + struct _Unwind_Context *context = (struct _Unwind_Context *)&cursor; + size_t off; + size_t len; + uint32_t* unwindInfo = (uint32_t *) frameInfo.unwind_info; + unwindInfo = decode_eht_entry(unwindInfo, &off, &len); + if (unwindInfo == NULL) { + return _URC_FAILURE; + } + + result = _Unwind_VRS_Interpret(context, unwindInfo, off, len); + if (result != _URC_CONTINUE_UNWIND) { + return _URC_END_OF_STACK; + } +#endif // LIBCXXABI_ARM_EHABI + // debugging if (_LIBUNWIND_TRACING_UNWINDING) { char functionName[512]; @@ -133,8 +156,7 @@ } // call trace function with this frame - _Unwind_Reason_Code result = - (*callback)((struct _Unwind_Context *)(&cursor), ref); + result = (*callback)((struct _Unwind_Context *)(&cursor), ref); if (result != _URC_NO_REASON) { _LIBUNWIND_TRACE_UNWINDING(" _backtrace: ended because callback " "returned %d\n", Index: libcxxabi/trunk/src/Unwind/libunwind_ext.h =================================================================== --- libcxxabi/trunk/src/Unwind/libunwind_ext.h +++ libcxxabi/trunk/src/Unwind/libunwind_ext.h @@ -13,7 +13,9 @@ #ifndef __LIBUNWIND_EXT__ #define __LIBUNWIND_EXT__ +#include "config.h" #include +#include #define UNW_STEP_SUCCESS 1 #define UNW_STEP_END 0 @@ -31,6 +33,13 @@ extern void _unw_add_dynamic_fde(unw_word_t fde); extern void _unw_remove_dynamic_fde(unw_word_t fde); +#if LIBCXXABI_ARM_EHABI +extern const uint32_t* decode_eht_entry(const uint32_t*, size_t*, size_t*); +extern _Unwind_Reason_Code _Unwind_VRS_Interpret(_Unwind_Context *context, + const uint32_t *data, + size_t offset, size_t len); +#endif + #ifdef __cplusplus } #endif Index: libcxxabi/trunk/src/cxa_personality.cpp =================================================================== --- libcxxabi/trunk/src/cxa_personality.cpp +++ libcxxabi/trunk/src/cxa_personality.cpp @@ -12,14 +12,19 @@ // //===----------------------------------------------------------------------===// +#include +#include +#include + #include "config.h" -#include "unwind.h" #include "cxa_exception.hpp" #include "cxa_handlers.hpp" #include "private_typeinfo.h" -#include -#include -#include +#include "unwind.h" + +#if LIBCXXABI_ARM_EHABI +#include "Unwind/libunwind_ext.h" +#endif /* Exception Header Layout: Index: libcxxabi/trunk/test/backtrace_test.cpp =================================================================== --- libcxxabi/trunk/test/backtrace_test.cpp +++ libcxxabi/trunk/test/backtrace_test.cpp @@ -0,0 +1,61 @@ +//===---------------------- backtrace_test.cpp ----------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include +#include + +extern "C" _Unwind_Reason_Code +trace_function(struct _Unwind_Context* context, void* ntraced) { + (*reinterpret_cast(ntraced))++; + // We should never have a call stack this deep... + assert(*reinterpret_cast(ntraced) < 20); + return _URC_NO_REASON; +} + +void call3_throw(size_t* ntraced) { + try { + _Unwind_Backtrace(trace_function, ntraced); + } catch (...) { + assert(false); + } +} + +void call3_nothrow(size_t* ntraced) { + _Unwind_Backtrace(trace_function, ntraced); +} + +void call2(size_t* ntraced, bool do_throw) { + if (do_throw) { + call3_throw(ntraced); + } else { + call3_nothrow(ntraced); + } +} + +void call1(size_t* ntraced, bool do_throw) { + call2(ntraced, do_throw); +} + +int main() { + size_t throw_ntraced = 0; + size_t nothrow_ntraced = 0; + + call1(¬hrow_ntraced, false); + + try { + call1(&throw_ntraced, true); + } catch (...) { + assert(false); + } + + // Different platforms (and different runtimes) will unwind a different number + // of times, so we can't make any better assumptions than this. + assert(nothrow_ntraced > 1); + assert(throw_ntraced == nothrow_ntraced); // Make sure we unwind through catch + return 0; +}