Make _Unwind_Backtrace() work on ARM.

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

llvm-svn: 216730
This commit is contained in:
Dan Albert 2014-08-29 15:26:06 +00:00
parent f28e7e7d34
commit 2c012d495d
6 changed files with 152 additions and 31 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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",

View File

@ -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

View File

@ -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:

View File

@ -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;
}