Index: include/unwind.h =================================================================== --- include/unwind.h +++ include/unwind.h @@ -207,6 +207,7 @@ uint32_t discriminator, _Unwind_VRS_DataRepresentation representation); +extern uint32_t* decode_eht_entry(const uint32_t*, size_t*, size_t*); extern _Unwind_Reason_Code _Unwind_VRS_Interpret(_Unwind_Context *context, uint32_t *data, size_t offset, size_t len); Index: src/Unwind/Unwind-EHABI.cpp =================================================================== --- src/Unwind/Unwind-EHABI.cpp +++ src/Unwind/Unwind-EHABI.cpp @@ -167,24 +167,13 @@ 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"); 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 +187,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,6 +204,43 @@ } // 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" 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 const_cast(data); +} + _Unwind_Reason_Code _Unwind_VRS_Interpret( _Unwind_Context* context, uint32_t* data, Index: src/Unwind/UnwindLevel1-gcc-ext.c =================================================================== --- src/Unwind/UnwindLevel1-gcc-ext.c +++ src/Unwind/UnwindLevel1-gcc-ext.c @@ -109,6 +109,28 @@ // walk each frame while (true) { + _Unwind_Reason_Code result; +#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 // ask libuwind to get next frame (skip over first frame which is // _Unwind_Backtrace()) @@ -133,8 +155,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: test/backtrace_test.cpp =================================================================== --- /dev/null +++ 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); + } + + // Can we be more precise? Will the number of traces be the same on all + // platforms? + assert(nothrow_ntraced > 1); + assert(throw_ntraced == nothrow_ntraced); // Make sure we unwind through catch + return 0; +}