forked from OSchip/llvm-project
libunwind: Fix unw_step() for ARM EHABI.
This commit fixes the unw_step() for ARM EHABI. However, this commit also changes the implementation details for ARM EHABI. The first change is that the personality function should call __gnu_unwind_frame() for default (or de facto) frame unwinding based on the ARM-defined unwind opcode. The function __gnu_unwind_frame() will in turn calls unw_step() which actually unwinds the frame. The second change is that the implementation _Unwind_Backtrace() should no longer calls unw_step() to unwind the frame; since according to ARM EHABI, the personality function should unwind the frame for us. Special thanks to Anton for helpful suggestion on the initial version of this patch. llvm-svn: 238560
This commit is contained in:
parent
f4b4430f8c
commit
7fab97f364
|
@ -168,25 +168,22 @@ static _Unwind_Reason_Code unwindOneFrame(_Unwind_State state,
|
|||
assert((*unwindingData & 0xf0000000) == 0x80000000 && "Must be a compact entry");
|
||||
Descriptor::Format format =
|
||||
static_cast<Descriptor::Format>((*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<const char *>(_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<const char*>(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<unw_cursor_t*>(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 @@ uint32_t RegisterRange(uint8_t start, uint8_t count_minus_one) {
|
|||
*/
|
||||
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<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;
|
||||
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<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;
|
||||
}
|
||||
|
@ -443,6 +451,7 @@ unwind_phase1(unw_context_t *uc, _Unwind_Exception *exception_object) {
|
|||
// 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 @@ unwind_phase1(unw_context_t *uc, _Unwind_Exception *exception_object) {
|
|||
static_cast<void *>(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 @@ static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc,
|
|||
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 _Unwind_Reason_Code unwind_phase2(unw_context_t *uc,
|
|||
static_cast<void *>(exception_object));
|
||||
return _URC_FATAL_PHASE2_ERROR;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Get info about this frame.
|
||||
unw_word_t sp;
|
||||
|
@ -752,11 +764,6 @@ _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) {
|
|||
_LIBUNWIND_TRACE_API(
|
||||
"_Unwind_GetLanguageSpecificData(context=%p) => 0x%llx\n",
|
||||
static_cast<void *>(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 @@ _Unwind_DeleteException(_Unwind_Exception *exception_object) {
|
|||
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
|
||||
|
|
|
@ -440,6 +440,20 @@ private:
|
|||
|
||||
#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<const uint32_t *>(_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 @@ bool UnwindCursor<A, R>::getInfoFromEHABISection(
|
|||
// 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 @@ bool UnwindCursor<A, R>::getInfoFromEHABISection(
|
|||
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 @@ int UnwindCursor<A, R>::step() {
|
|||
#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 \
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "libunwind_ext.h"
|
||||
|
@ -110,10 +111,18 @@ _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) {
|
|||
_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 @@ _Unwind_Backtrace(_Unwind_Trace_Fn callback, void *ref) {
|
|||
_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.
|
||||
ex.pr_cache.fnstart = frameInfo.start_ip;
|
||||
ex.pr_cache.ehtp = (_Unwind_EHT_Header *) unwindInfo;
|
||||
ex.pr_cache.additional= frameInfo.flags;
|
||||
|
||||
// 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;
|
||||
}
|
||||
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
|
||||
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
#include <libunwind.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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);
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <unwind.h>
|
||||
|
||||
#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);
|
||||
}
|
Loading…
Reference in New Issue