summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Albert <danalbert@google.com>2014-08-29 15:26:06 +0000
committerDan Albert <danalbert@google.com>2014-08-29 09:04:51 -0700
commit605f03260ce43ec564cd0ec80ea21514e82eeeb3 (patch)
tree2939a54ccd493edda8203712f63bf1c3a286c51a
parent4c708dc711f4bf00e6ff03831d316f7df239f61e (diff)
downloadlibcxxabi-lollipop-mr1-dev.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.h4
-rw-r--r--src/Unwind/Unwind-EHABI.cpp70
-rw-r--r--src/Unwind/UnwindLevel1-gcc-ext.c26
-rw-r--r--src/Unwind/libunwind_ext.h9
-rw-r--r--src/cxa_personality.cpp13
-rw-r--r--test/backtrace_test.cpp61
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(&nothrow_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;
+}