Index: src/Unwind-EHABI.cpp =================================================================== --- src/Unwind-EHABI.cpp +++ src/Unwind-EHABI.cpp @@ -168,25 +168,22 @@ assert((*unwindingData & 0xf0000000) == 0x80000000 && "Must be a compact entry"); Descriptor::Format format = static_cast((*unwindingData & 0x0f000000) >> 24); - size_t len = 0; - size_t off = 0; - unwindingData = decode_eht_entry(unwindingData, &off, &len); - if (unwindingData == nullptr) { - return _URC_FAILURE; - } + + const char *lsda = + reinterpret_cast(_Unwind_GetLanguageSpecificData(context)); // Handle descriptors before unwinding so they are processed in the context // of the correct stack frame. _Unwind_Reason_Code result = - ProcessDescriptors( - state, ucbp, context, format, - reinterpret_cast(ucbp->pr_cache.ehtp) + len, - ucbp->pr_cache.additional); + ProcessDescriptors(state, ucbp, context, format, lsda, + ucbp->pr_cache.additional); if (result != _URC_CONTINUE_UNWIND) return result; - return _Unwind_VRS_Interpret(context, unwindingData, off, len); + if (unw_step(reinterpret_cast(context)) != UNW_STEP_SUCCESS) + return _URC_FAILURE; + return _URC_CONTINUE_UNWIND; } // Generates mask discriminator for _Unwind_VRS_Pop, e.g. for _UVRSC_CORE / @@ -213,26 +210,37 @@ */ extern "C" const uint32_t* decode_eht_entry(const uint32_t* data, size_t* off, size_t* len) { - assert((*data & 0x80000000) != 0 && - "decode_eht_entry() does not support user-defined personality"); - - // 6.3: ARM Compact Model - // EHT entries here correspond to the __aeabi_unwind_cpp_pr[012] PRs indeded - // by format: - 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; + if ((*data & 0x80000000) == 0) { + // 6.2: Generic Model + // + // EHT entry is a prel31 pointing to the PR, followed by data understood + // only by the personality routine. Fortunately, all existing assembler + // implementations, including GNU assembler, LLVM integrated assembler, + // and ARM assembler, assume that the unwind opcodes come after the + // personality rountine address. + *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 + // + // EHT entries here correspond to the __aeabi_unwind_cpp_pr[012] PRs indeded + // by format: + 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; } @@ -443,6 +451,7 @@ // Walk each frame looking for a place to stop. for (bool handlerNotFound = true; handlerNotFound;) { +#if !LIBCXXABI_ARM_EHABI // Ask libuwind to get next frame (skip over first which is // _Unwind_RaiseException). int stepResult = unw_step(&cursor1); @@ -457,6 +466,7 @@ static_cast(exception_object)); return _URC_FATAL_PHASE1_ERROR; } +#endif // See if frame has code to run (has personality routine). unw_proc_info_t frameInfo; @@ -575,6 +585,7 @@ resume = false; } +#if !LIBCXXABI_ARM_EHABI int stepResult = unw_step(&cursor2); if (stepResult == 0) { _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached " @@ -587,6 +598,7 @@ static_cast(exception_object)); return _URC_FATAL_PHASE2_ERROR; } +#endif // Get info about this frame. unw_word_t sp; @@ -752,11 +764,6 @@ _LIBUNWIND_TRACE_API( "_Unwind_GetLanguageSpecificData(context=%p) => 0x%llx\n", static_cast(context), (long long)result); - if (result != 0) { - if (*((uint8_t *)result) != 0xFF) - _LIBUNWIND_DEBUG_LOG("lsda at 0x%llx does not start with 0xFF\n", - (long long)result); - } return result; } @@ -990,4 +997,13 @@ exception_object); } +_LIBUNWIND_EXPORT extern "C" _Unwind_Reason_Code +__gnu_unwind_frame(_Unwind_Exception *exception_object, + struct _Unwind_Context *context) { + unw_cursor_t *cursor = (unw_cursor_t *)context; + if (unw_step(cursor) != UNW_STEP_SUCCESS) + return _URC_FAILURE; + return _URC_OK; +} + #endif // LIBCXXABI_ARM_EHABI Index: src/UnwindCursor.hpp =================================================================== --- src/UnwindCursor.hpp +++ src/UnwindCursor.hpp @@ -440,6 +440,20 @@ #if LIBCXXABI_ARM_EHABI bool getInfoFromEHABISection(pint_t pc, const UnwindInfoSections §s); + + int stepWithEHABI() { + size_t len = 0; + size_t off = 0; + // FIXME: Calling decode_eht_entry() here is violating the libunwind + // abstraction layer. + const uint32_t *ehtp = + decode_eht_entry(reinterpret_cast(_info.unwind_info), + &off, &len); + if (_Unwind_VRS_Interpret((_Unwind_Context *)this, ehtp, off, len) != + _URC_CONTINUE_UNWIND) + return UNW_STEP_END; + return UNW_STEP_SUCCESS; + } #endif #if _LIBUNWIND_SUPPORT_DWARF_UNWIND @@ -731,7 +745,7 @@ // isSingleWordEHT -- whether the entry is in the index. unw_word_t personalityRoutine = 0xbadf00d; bool scope32 = false; - uintptr_t lsda = 0xbadf00d; + uintptr_t lsda; // If the high bit in the exception handling table entry is set, the entry is // in compact form (section 6.3 EHABI). @@ -744,16 +758,19 @@ personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr0; extraWords = 0; scope32 = false; + lsda = isSingleWordEHT ? 0 : (exceptionTableAddr + 4); break; case 1: personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr1; extraWords = (exceptionTableData & 0x00ff0000) >> 16; scope32 = false; + lsda = exceptionTableAddr + (extraWords + 1) * 4; break; case 2: personalityRoutine = (unw_word_t) &__aeabi_unwind_cpp_pr2; extraWords = (exceptionTableData & 0x00ff0000) >> 16; scope32 = true; + lsda = exceptionTableAddr + (extraWords + 1) * 4; break; default: _LIBUNWIND_ABORT("unknown personality routine"); @@ -1281,7 +1298,7 @@ #elif _LIBUNWIND_SUPPORT_DWARF_UNWIND result = this->stepWithDwarfFDE(); #elif LIBCXXABI_ARM_EHABI - result = UNW_STEP_SUCCESS; + result = this->stepWithEHABI(); #else #error Need _LIBUNWIND_SUPPORT_COMPACT_UNWIND or \ _LIBUNWIND_SUPPORT_DWARF_UNWIND or \ Index: src/UnwindLevel1-gcc-ext.c =================================================================== --- src/UnwindLevel1-gcc-ext.c +++ src/UnwindLevel1-gcc-ext.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "config.h" #include "libunwind_ext.h" @@ -110,10 +111,18 @@ _LIBUNWIND_TRACE_API("_Unwind_Backtrace(callback=%p)\n", (void *)(uintptr_t)callback); +#if LIBCXXABI_ARM_EHABI + // Create a mock exception object for force unwinding. + _Unwind_Exception ex; + memset(&ex, '\0', sizeof(ex)); + ex.exception_class = 0x434C4E47554E5700; // CLNGUNW\0 +#endif + // walk each frame while (true) { _Unwind_Reason_Code result; +#if !LIBCXXABI_ARM_EHABI // ask libuwind to get next frame (skip over first frame which is // _Unwind_Backtrace()) if (unw_step(&cursor) <= 0) { @@ -122,48 +131,28 @@ _URC_END_OF_STACK); return _URC_END_OF_STACK; } - -#if LIBCXXABI_ARM_EHABI +#else // 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; + // Update the pr_cache in the mock exception object. const uint32_t* unwindInfo = (uint32_t *) frameInfo.unwind_info; - if ((*unwindInfo & 0x80000000) == 0) { - // 6.2: Generic Model - // EHT entry is a prel31 pointing to the PR, followed by data understood - // only by the personality routine. Since EHABI doesn't guarantee the - // location or availability of the unwind opcodes in the generic model, - // we have to call personality functions with (_US_VIRTUAL_UNWIND_FRAME | - // _US_FORCE_UNWIND) state. - - // Create a mock exception object for force unwinding. - _Unwind_Exception ex; - ex.exception_class = 0x434C4E47554E5700; // CLNGUNW\0 - ex.pr_cache.fnstart = frameInfo.start_ip; - ex.pr_cache.ehtp = (_Unwind_EHT_Header *) unwindInfo; - ex.pr_cache.additional= frameInfo.flags; - - // Get and call the personality function to unwind the frame. - __personality_routine pr = (__personality_routine) readPrel31(unwindInfo); - if (pr(_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, &ex, context) != - _URC_CONTINUE_UNWIND) { - return _URC_END_OF_STACK; - } - } else { - size_t off, len; - 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; - } + ex.pr_cache.fnstart = frameInfo.start_ip; + ex.pr_cache.ehtp = (_Unwind_EHT_Header *) unwindInfo; + ex.pr_cache.additional= frameInfo.flags; + + struct _Unwind_Context *context = (struct _Unwind_Context *)&cursor; + // Get and call the personality function to unwind the frame. + __personality_routine handler = (__personality_routine) frameInfo.handler; + if (handler == NULL) { + return _URC_END_OF_STACK; + } + if (handler(_US_VIRTUAL_UNWIND_FRAME | _US_FORCE_UNWIND, &ex, context) != + _URC_CONTINUE_UNWIND) { + return _URC_END_OF_STACK; } #endif // LIBCXXABI_ARM_EHABI Index: test/libunwind_01.pass.cpp =================================================================== --- /dev/null +++ test/libunwind_01.pass.cpp @@ -0,0 +1,42 @@ +#include +#include + +void backtrace(int lower_bound) { + unw_context_t context; + unw_getcontext(&context); + + unw_cursor_t cursor; + unw_init_local(&cursor, &context); + + int n = 0; + do { + ++n; + if (n > 100) { + abort(); + } + } while (unw_step(&cursor) > 0); + + if (n < lower_bound) { + abort(); + } +} + +void test1(int i) { + backtrace(i); +} + +void test2(int i, int j) { + backtrace(i); + test1(j); +} + +void test3(int i, int j, int k) { + backtrace(i); + test2(j, k); +} + +int main() { + test1(1); + test2(1, 2); + test3(1, 2, 3); +} Index: test/libunwind_02.pass.cpp =================================================================== --- /dev/null +++ test/libunwind_02.pass.cpp @@ -0,0 +1,37 @@ +#include +#include +#include + +#define EXPECTED_NUM_FRAMES 50 +#define NUM_FRAMES_UPPER_BOUND 100 + +_Unwind_Reason_Code callback(_Unwind_Context *context, void *cnt) { + int *i = (int *)cnt; + ++*i; + if (*i > NUM_FRAMES_UPPER_BOUND) { + abort(); + } + return _URC_NO_REASON; +} + +void test_backtrace() { + int n = 0; + _Unwind_Backtrace(&callback, &n); + if (n < EXPECTED_NUM_FRAMES) { + abort(); + } +} + +int test(int i) { + if (i == 0) { + test_backtrace(); + return 0; + } else { + return i + test(i - 1); + } +} + +int main() { + int total = test(50); + assert(total == 1275); +}