diff options
author | Dan Albert <danalbert@google.com> | 2014-08-29 15:26:06 +0000 |
---|---|---|
committer | Dan Albert <danalbert@google.com> | 2014-08-29 09:04:51 -0700 |
commit | 605f03260ce43ec564cd0ec80ea21514e82eeeb3 (patch) | |
tree | 2939a54ccd493edda8203712f63bf1c3a286c51a | |
parent | 4c708dc711f4bf00e6ff03831d316f7df239f61e (diff) | |
download | libcxxabi-android-5.1.1_r18.tar.gz |
Make _Unwind_Backtrace() work on ARM.android-cts-5.1_r9android-cts-5.1_r8android-cts-5.1_r7android-cts-5.1_r6android-cts-5.1_r5android-cts-5.1_r4android-cts-5.1_r3android-cts-5.1_r28android-cts-5.1_r27android-cts-5.1_r26android-cts-5.1_r25android-cts-5.1_r24android-cts-5.1_r23android-cts-5.1_r22android-cts-5.1_r21android-cts-5.1_r20android-cts-5.1_r2android-cts-5.1_r19android-cts-5.1_r18android-cts-5.1_r17android-cts-5.1_r16android-cts-5.1_r15android-cts-5.1_r14android-cts-5.1_r13android-cts-5.1_r10android-cts-5.1_r1android-cts-5.0_r9android-cts-5.0_r8android-cts-5.0_r7android-cts-5.0_r6android-cts-5.0_r5android-cts-5.0_r4android-cts-5.0_r3android-5.1.1_r9android-5.1.1_r8android-5.1.1_r7android-5.1.1_r6android-5.1.1_r5android-5.1.1_r4android-5.1.1_r38android-5.1.1_r37android-5.1.1_r36android-5.1.1_r35android-5.1.1_r34android-5.1.1_r33android-5.1.1_r30android-5.1.1_r3android-5.1.1_r29android-5.1.1_r28android-5.1.1_r26android-5.1.1_r25android-5.1.1_r24android-5.1.1_r23android-5.1.1_r22android-5.1.1_r20android-5.1.1_r2android-5.1.1_r19android-5.1.1_r18android-5.1.1_r17android-5.1.1_r16android-5.1.1_r15android-5.1.1_r14android-5.1.1_r13android-5.1.1_r12android-5.1.1_r10android-5.1.1_r1android-5.1.0_r5android-5.1.0_r4android-5.1.0_r3android-5.1.0_r1android-5.0.2_r3android-5.0.2_r1android-5.0.1_r1android-5.0.0_r7android-5.0.0_r6android-5.0.0_r5.1android-5.0.0_r5android-5.0.0_r4android-5.0.0_r3android-5.0.0_r2android-5.0.0_r1lollipop-releaselollipop-mr1-wfc-releaselollipop-mr1-releaselollipop-mr1-fi-releaselollipop-mr1-devlollipop-mr1-cts-releaselollipop-devlollipop-cts-release
Summary: Since the personality functions do the actual unwinding on ARM,
and will also stop unwinding when they encounter a handler, we invoke
_Unwind_VRS_Interpret() directly form _Unwind_Backtrace().
To simplify, the logic for decoding an EHT is moved out of
unwindOneFrame() and into its own function, decode_eht_entry(). Unlike
unwindOneFrame(), which could only handle ARM's compact personality
function entries (section 6.3) decode_eht_entry() can handle the generic
entries (section 6.2).
Reviewers: jroelofs
Reviewed By: jroelofs
Subscribers: piman, aemerson, cfe-commits
Differential Revision: http://reviews.llvm.org/D5112
git-svn-id: https://llvm.org/svn/llvm-project/libcxxabi/trunk@216730 91177308-0d34-0410-b5e6-96231b3b80d8
(cherry picked from commit 14690900fcd84fbf82767890f4d7a5673654fe38)
Bug: 16874447
Change-Id: Ia52c7e2a0b7167863fea29cbf4b2d3c5d58053b2
-rw-r--r-- | include/unwind.h | 4 | ||||
-rw-r--r-- | src/Unwind/Unwind-EHABI.cpp | 70 | ||||
-rw-r--r-- | src/Unwind/UnwindLevel1-gcc-ext.c | 26 | ||||
-rw-r--r-- | src/Unwind/libunwind_ext.h | 9 | ||||
-rw-r--r-- | src/cxa_personality.cpp | 13 | ||||
-rw-r--r-- | test/backtrace_test.cpp | 61 |
6 files changed, 152 insertions, 31 deletions
diff --git a/include/unwind.h b/include/unwind.h index 4e77c3a..dd82bc9 100644 --- a/include/unwind.h +++ b/include/unwind.h @@ -207,10 +207,6 @@ _Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, 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; diff --git a/src/Unwind/Unwind-EHABI.cpp b/src/Unwind/Unwind-EHABI.cpp index 9400713..84dd0d1 100644 --- a/src/Unwind/Unwind-EHABI.cpp +++ b/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 @@ namespace { // 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<uint8_t*>(data); +uint8_t getByte(const uint32_t* data, size_t offset) { + const uint8_t* byteData = reinterpret_cast<const uint8_t*>(data); return byteData[(offset & ~(size_t)0x03) + (3 - (offset & (size_t)0x03))]; } @@ -166,25 +167,15 @@ _Unwind_Reason_Code unwindOneFrame( _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<Descriptor::Format>((unwindInfo & 0x0f000000) >> 24); + static_cast<Descriptor::Format>((*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 @@ _Unwind_Reason_Code unwindOneFrame( 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 @@ uint32_t RegisterRange(uint8_t start, uint8_t count_minus_one) { } // 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<Descriptor::Format>((*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; diff --git a/src/Unwind/UnwindLevel1-gcc-ext.c b/src/Unwind/UnwindLevel1-gcc-ext.c index 745c309..601b302 100644 --- a/src/Unwind/UnwindLevel1-gcc-ext.c +++ b/src/Unwind/UnwindLevel1-gcc-ext.c @@ -109,6 +109,7 @@ _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { // 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 @@ _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { 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 @@ _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) { } // 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", diff --git a/src/Unwind/libunwind_ext.h b/src/Unwind/libunwind_ext.h index 38d71cc..5eb0e87 100644 --- a/src/Unwind/libunwind_ext.h +++ b/src/Unwind/libunwind_ext.h @@ -13,7 +13,9 @@ #ifndef __LIBUNWIND_EXT__ #define __LIBUNWIND_EXT__ +#include "config.h" #include <libunwind.h> +#include <unwind.h> #define UNW_STEP_SUCCESS 1 #define UNW_STEP_END 0 @@ -31,6 +33,13 @@ extern void unw_iterate_dwarf_unwind_cache(void (*func)(unw_word_t ip_start, 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 diff --git a/src/cxa_personality.cpp b/src/cxa_personality.cpp index c2ee3fd..c356250 100644 --- a/src/cxa_personality.cpp +++ b/src/cxa_personality.cpp @@ -12,14 +12,19 @@ // //===----------------------------------------------------------------------===// +#include <assert.h> +#include <stdlib.h> +#include <typeinfo> + #include "config.h" -#include "unwind.h" #include "cxa_exception.hpp" #include "cxa_handlers.hpp" #include "private_typeinfo.h" -#include <typeinfo> -#include <stdlib.h> -#include <assert.h> +#include "unwind.h" + +#if LIBCXXABI_ARM_EHABI +#include "Unwind/libunwind_ext.h" +#endif /* Exception Header Layout: diff --git a/test/backtrace_test.cpp b/test/backtrace_test.cpp new file mode 100644 index 0000000..25dccb3 --- /dev/null +++ b/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 <assert.h> +#include <unwind.h> + +extern "C" _Unwind_Reason_Code +trace_function(struct _Unwind_Context* context, void* ntraced) { + (*reinterpret_cast<size_t*>(ntraced))++; + // We should never have a call stack this deep... + assert(*reinterpret_cast<size_t*>(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; +} |