| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include "Unwind-EHABI.h" |
| |
|
| | #if defined(_LIBUNWIND_ARM_EHABI) |
| |
|
| | #include <inttypes.h> |
| | #include <stdbool.h> |
| | #include <stdint.h> |
| | #include <stdio.h> |
| | #include <stdlib.h> |
| | #include <string.h> |
| |
|
| | #include "config.h" |
| | #include "libunwind.h" |
| | #include "libunwind_ext.h" |
| | #include "unwind.h" |
| |
|
| | namespace { |
| |
|
| | |
| | |
| | uint8_t getByte(const uint32_t* data, size_t offset) { |
| | const uint8_t* byteData = reinterpret_cast<const uint8_t*>(data); |
| | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| | return byteData[(offset & ~(size_t)0x03) + (3 - (offset & (size_t)0x03))]; |
| | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| | return byteData[offset]; |
| | #else |
| | #error "Unable to determine endianess" |
| | #endif |
| | } |
| |
|
| | const char* getNextWord(const char* data, uint32_t* out) { |
| | *out = *reinterpret_cast<const uint32_t*>(data); |
| | return data + 4; |
| | } |
| |
|
| | const char* getNextNibble(const char* data, uint32_t* out) { |
| | *out = *reinterpret_cast<const uint16_t*>(data); |
| | return data + 2; |
| | } |
| |
|
| | struct Descriptor { |
| | |
| | typedef enum { |
| | SU16 = 0, |
| | LU16 = 1, |
| | LU32 = 3, |
| | RESERVED0 = 4, RESERVED1 = 5, RESERVED2 = 6, RESERVED3 = 7, |
| | RESERVED4 = 8, RESERVED5 = 9, RESERVED6 = 10, RESERVED7 = 11, |
| | RESERVED8 = 12, RESERVED9 = 13, RESERVED10 = 14, RESERVED11 = 15 |
| | } Format; |
| |
|
| | |
| | typedef enum { |
| | CLEANUP = 0x0, |
| | FUNC = 0x1, |
| | CATCH = 0x2, |
| | INVALID = 0x4 |
| | } Kind; |
| | }; |
| |
|
| | _Unwind_Reason_Code ProcessDescriptors( |
| | _Unwind_State state, |
| | _Unwind_Control_Block* ucbp, |
| | struct _Unwind_Context* context, |
| | Descriptor::Format format, |
| | const char* descriptorStart, |
| | uint32_t flags) { |
| |
|
| | |
| | if (flags & 0x1) |
| | return _URC_CONTINUE_UNWIND; |
| |
|
| | |
| | |
| | (void)state; |
| |
|
| | const char* descriptor = descriptorStart; |
| | uint32_t descriptorWord; |
| | getNextWord(descriptor, &descriptorWord); |
| | while (descriptorWord) { |
| | |
| | uint32_t length; |
| | uint32_t offset; |
| | switch (format) { |
| | case Descriptor::LU32: |
| | descriptor = getNextWord(descriptor, &length); |
| | descriptor = getNextWord(descriptor, &offset); |
| | case Descriptor::LU16: |
| | descriptor = getNextNibble(descriptor, &length); |
| | descriptor = getNextNibble(descriptor, &offset); |
| | default: |
| | assert(false); |
| | return _URC_FAILURE; |
| | } |
| |
|
| | |
| | Descriptor::Kind kind = |
| | static_cast<Descriptor::Kind>((length & 0x1) | ((offset & 0x1) << 1)); |
| |
|
| | |
| | length &= ~1u; |
| | offset &= ~1u; |
| | uintptr_t scopeStart = ucbp->pr_cache.fnstart + offset; |
| | uintptr_t scopeEnd = scopeStart + length; |
| | uintptr_t pc = _Unwind_GetIP(context); |
| | bool isInScope = (scopeStart <= pc) && (pc < scopeEnd); |
| |
|
| | switch (kind) { |
| | case Descriptor::CLEANUP: { |
| | |
| | break; |
| | } |
| | case Descriptor::FUNC: { |
| | |
| | break; |
| | } |
| | case Descriptor::CATCH: { |
| | |
| | uint32_t landing_pad; |
| | descriptor = getNextWord(descriptor, &landing_pad); |
| |
|
| | if (isInScope) { |
| | |
| | |
| | landing_pad = signExtendPrel31(landing_pad & ~0x80000000); |
| | if (landing_pad == 0xffffffff) { |
| | return _URC_HANDLER_FOUND; |
| | } else if (landing_pad == 0xfffffffe) { |
| | return _URC_FAILURE; |
| | } else { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | _LIBUNWIND_ABORT("Type matching not implemented"); |
| | } |
| | } |
| | break; |
| | } |
| | default: |
| | _LIBUNWIND_ABORT("Invalid descriptor kind found."); |
| | } |
| |
|
| | getNextWord(descriptor, &descriptorWord); |
| | } |
| |
|
| | return _URC_CONTINUE_UNWIND; |
| | } |
| |
|
| | static _Unwind_Reason_Code unwindOneFrame(_Unwind_State state, |
| | _Unwind_Control_Block* ucbp, |
| | struct _Unwind_Context* context) { |
| | |
| | const uint32_t* unwindingData = ucbp->pr_cache.ehtp; |
| | assert((*unwindingData & 0xf0000000) == 0x80000000 && "Must be a compact entry"); |
| | Descriptor::Format format = |
| | static_cast<Descriptor::Format>((*unwindingData & 0x0f000000) >> 24); |
| |
|
| | const char *lsda = |
| | reinterpret_cast<const char *>(_Unwind_GetLanguageSpecificData(context)); |
| |
|
| | |
| | |
| | _Unwind_Reason_Code result = |
| | ProcessDescriptors(state, ucbp, context, format, lsda, |
| | ucbp->pr_cache.additional); |
| |
|
| | if (result != _URC_CONTINUE_UNWIND) |
| | return result; |
| |
|
| | if (__unw_step(reinterpret_cast<unw_cursor_t *>(context)) != UNW_STEP_SUCCESS) |
| | return _URC_FAILURE; |
| | return _URC_CONTINUE_UNWIND; |
| | } |
| |
|
| | |
| | |
| | uint32_t RegisterMask(uint8_t start, uint8_t count_minus_one) { |
| | return ((1U << (count_minus_one + 1)) - 1) << start; |
| | } |
| |
|
| | |
| | |
| | uint32_t RegisterRange(uint8_t start, uint8_t count_minus_one) { |
| | return ((uint32_t)start << 16) | ((uint32_t)count_minus_one + 1); |
| | } |
| |
|
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | extern "C" const uint32_t* |
| | decode_eht_entry(const uint32_t* data, size_t* off, size_t* len) { |
| | if ((*data & 0x80000000) == 0) { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | *off = 1; |
| | *len = (((data[1] >> 24) & 0xff) + 1) * 4; |
| | data++; |
| | } else { |
| | |
| | |
| | |
| | |
| | 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; |
| | } |
| |
|
| | _LIBUNWIND_EXPORT _Unwind_Reason_Code |
| | _Unwind_VRS_Interpret(_Unwind_Context *context, const uint32_t *data, |
| | size_t offset, size_t len) { |
| | bool wrotePC = false; |
| | bool finish = false; |
| | while (offset < len && !finish) { |
| | uint8_t byte = getByte(data, offset++); |
| | if ((byte & 0x80) == 0) { |
| | uint32_t sp; |
| | _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp); |
| | if (byte & 0x40) |
| | sp -= (((uint32_t)byte & 0x3f) << 2) + 4; |
| | else |
| | sp += ((uint32_t)byte << 2) + 4; |
| | _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, &sp); |
| | } else { |
| | switch (byte & 0xf0) { |
| | case 0x80: { |
| | if (offset >= len) |
| | return _URC_FAILURE; |
| | uint32_t registers = |
| | (((uint32_t)byte & 0x0f) << 12) | |
| | (((uint32_t)getByte(data, offset++)) << 4); |
| | if (!registers) |
| | return _URC_FAILURE; |
| | if (registers & (1 << 15)) |
| | wrotePC = true; |
| | _Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32); |
| | break; |
| | } |
| | case 0x90: { |
| | uint8_t reg = byte & 0x0f; |
| | if (reg == 13 || reg == 15) |
| | return _URC_FAILURE; |
| | uint32_t sp; |
| | _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_R0 + reg, |
| | _UVRSD_UINT32, &sp); |
| | _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, |
| | &sp); |
| | break; |
| | } |
| | case 0xa0: { |
| | uint32_t registers = RegisterMask(4, byte & 0x07); |
| | if (byte & 0x08) |
| | registers |= 1 << 14; |
| | _Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32); |
| | break; |
| | } |
| | case 0xb0: { |
| | switch (byte) { |
| | case 0xb0: |
| | finish = true; |
| | break; |
| | case 0xb1: { |
| | if (offset >= len) |
| | return _URC_FAILURE; |
| | uint8_t registers = getByte(data, offset++); |
| | if (registers & 0xf0 || !registers) |
| | return _URC_FAILURE; |
| | _Unwind_VRS_Pop(context, _UVRSC_CORE, registers, _UVRSD_UINT32); |
| | break; |
| | } |
| | case 0xb2: { |
| | uint32_t addend = 0; |
| | uint32_t shift = 0; |
| | |
| | while (true) { |
| | if (offset >= len) |
| | return _URC_FAILURE; |
| | uint32_t v = getByte(data, offset++); |
| | addend |= (v & 0x7f) << shift; |
| | if ((v & 0x80) == 0) |
| | break; |
| | shift += 7; |
| | } |
| | uint32_t sp; |
| | _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, |
| | &sp); |
| | sp += 0x204 + (addend << 2); |
| | _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, |
| | &sp); |
| | break; |
| | } |
| | case 0xb3: { |
| | uint8_t v = getByte(data, offset++); |
| | _Unwind_VRS_Pop(context, _UVRSC_VFP, |
| | RegisterRange(static_cast<uint8_t>(v >> 4), |
| | v & 0x0f), _UVRSD_VFPX); |
| | break; |
| | } |
| | case 0xb4: |
| | case 0xb5: |
| | case 0xb6: |
| | case 0xb7: |
| | return _URC_FAILURE; |
| | default: |
| | _Unwind_VRS_Pop(context, _UVRSC_VFP, |
| | RegisterRange(8, byte & 0x07), _UVRSD_VFPX); |
| | break; |
| | } |
| | break; |
| | } |
| | case 0xc0: { |
| | switch (byte) { |
| | #if defined(__ARM_WMMX) |
| | case 0xc0: |
| | case 0xc1: |
| | case 0xc2: |
| | case 0xc3: |
| | case 0xc4: |
| | case 0xc5: |
| | _Unwind_VRS_Pop(context, _UVRSC_WMMXD, |
| | RegisterRange(10, byte & 0x7), _UVRSD_DOUBLE); |
| | break; |
| | case 0xc6: { |
| | uint8_t v = getByte(data, offset++); |
| | uint8_t start = static_cast<uint8_t>(v >> 4); |
| | uint8_t count_minus_one = v & 0xf; |
| | if (start + count_minus_one >= 16) |
| | return _URC_FAILURE; |
| | _Unwind_VRS_Pop(context, _UVRSC_WMMXD, |
| | RegisterRange(start, count_minus_one), |
| | _UVRSD_DOUBLE); |
| | break; |
| | } |
| | case 0xc7: { |
| | uint8_t v = getByte(data, offset++); |
| | if (!v || v & 0xf0) |
| | return _URC_FAILURE; |
| | _Unwind_VRS_Pop(context, _UVRSC_WMMXC, v, _UVRSD_DOUBLE); |
| | break; |
| | } |
| | #endif |
| | case 0xc8: |
| | case 0xc9: { |
| | uint8_t v = getByte(data, offset++); |
| | uint8_t start = |
| | static_cast<uint8_t>(((byte == 0xc8) ? 16 : 0) + (v >> 4)); |
| | uint8_t count_minus_one = v & 0xf; |
| | if (start + count_minus_one >= 32) |
| | return _URC_FAILURE; |
| | _Unwind_VRS_Pop(context, _UVRSC_VFP, |
| | RegisterRange(start, count_minus_one), |
| | _UVRSD_DOUBLE); |
| | break; |
| | } |
| | default: |
| | return _URC_FAILURE; |
| | } |
| | break; |
| | } |
| | case 0xd0: { |
| | if (byte & 0x08) |
| | return _URC_FAILURE; |
| | _Unwind_VRS_Pop(context, _UVRSC_VFP, RegisterRange(8, byte & 0x7), |
| | _UVRSD_DOUBLE); |
| | break; |
| | } |
| | default: |
| | return _URC_FAILURE; |
| | } |
| | } |
| | } |
| | if (!wrotePC) { |
| | uint32_t lr; |
| | _Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_LR, _UVRSD_UINT32, &lr); |
| | _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_IP, _UVRSD_UINT32, &lr); |
| | } |
| | return _URC_CONTINUE_UNWIND; |
| | } |
| |
|
| | extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code |
| | __aeabi_unwind_cpp_pr0(_Unwind_State state, _Unwind_Control_Block *ucbp, |
| | _Unwind_Context *context) { |
| | return unwindOneFrame(state, ucbp, context); |
| | } |
| |
|
| | extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code |
| | __aeabi_unwind_cpp_pr1(_Unwind_State state, _Unwind_Control_Block *ucbp, |
| | _Unwind_Context *context) { |
| | return unwindOneFrame(state, ucbp, context); |
| | } |
| |
|
| | extern "C" _LIBUNWIND_EXPORT _Unwind_Reason_Code |
| | __aeabi_unwind_cpp_pr2(_Unwind_State state, _Unwind_Control_Block *ucbp, |
| | _Unwind_Context *context) { |
| | return unwindOneFrame(state, ucbp, context); |
| | } |
| |
|
| | static _Unwind_Reason_Code |
| | unwind_phase1(unw_context_t *uc, unw_cursor_t *cursor, _Unwind_Exception *exception_object) { |
| | |
| | |
| | |
| | |
| | |
| | |
| | __unw_init_local(cursor, uc); |
| |
|
| | |
| | for (bool handlerNotFound = true; handlerNotFound;) { |
| |
|
| | |
| | unw_proc_info_t frameInfo; |
| | if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase1(ex_ojb=%p): __unw_get_proc_info " |
| | "failed => _URC_FATAL_PHASE1_ERROR", |
| | static_cast<void *>(exception_object)); |
| | return _URC_FATAL_PHASE1_ERROR; |
| | } |
| |
|
| | |
| | if (_LIBUNWIND_TRACING_UNWINDING) { |
| | char functionBuf[512]; |
| | const char *functionName = functionBuf; |
| | unw_word_t offset; |
| | if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), |
| | &offset) != UNW_ESUCCESS) || |
| | (frameInfo.start_ip + offset > frameInfo.end_ip)) |
| | functionName = ".anonymous."; |
| | unw_word_t pc; |
| | __unw_get_reg(cursor, UNW_REG_IP, &pc); |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase1(ex_ojb=%p): pc=0x%" PRIxPTR ", start_ip=0x%" PRIxPTR ", func=%s, " |
| | "lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR, |
| | static_cast<void *>(exception_object), pc, |
| | frameInfo.start_ip, functionName, |
| | frameInfo.lsda, frameInfo.handler); |
| | } |
| |
|
| | |
| | |
| | if (frameInfo.handler != 0) { |
| | _Unwind_Personality_Fn p = |
| | (_Unwind_Personality_Fn)(long)(frameInfo.handler); |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase1(ex_ojb=%p): calling personality function %p", |
| | static_cast<void *>(exception_object), |
| | reinterpret_cast<void *>(reinterpret_cast<uintptr_t>(p))); |
| | struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor); |
| | exception_object->pr_cache.fnstart = frameInfo.start_ip; |
| | exception_object->pr_cache.ehtp = |
| | (_Unwind_EHT_Header *)frameInfo.unwind_info; |
| | exception_object->pr_cache.additional = frameInfo.flags; |
| | _Unwind_Reason_Code personalityResult = |
| | (*p)(_US_VIRTUAL_UNWIND_FRAME, exception_object, context); |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase1(ex_ojb=%p): personality result %d start_ip %x ehtp %p " |
| | "additional %x", |
| | static_cast<void *>(exception_object), personalityResult, |
| | exception_object->pr_cache.fnstart, |
| | static_cast<void *>(exception_object->pr_cache.ehtp), |
| | exception_object->pr_cache.additional); |
| | switch (personalityResult) { |
| | case _URC_HANDLER_FOUND: |
| | |
| | |
| | handlerNotFound = false; |
| | |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND", |
| | static_cast<void *>(exception_object)); |
| | return _URC_NO_REASON; |
| |
|
| | case _URC_CONTINUE_UNWIND: |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND", |
| | static_cast<void *>(exception_object)); |
| | |
| | break; |
| |
|
| | |
| | case _URC_FAILURE: |
| | return _URC_FAILURE; |
| |
|
| | default: |
| | |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR", |
| | static_cast<void *>(exception_object)); |
| | return _URC_FATAL_PHASE1_ERROR; |
| | } |
| | } |
| | } |
| | return _URC_NO_REASON; |
| | } |
| |
|
| | static _Unwind_Reason_Code unwind_phase2(unw_context_t *uc, unw_cursor_t *cursor, |
| | _Unwind_Exception *exception_object, |
| | bool resume) { |
| | |
| | __unw_init_local(cursor, uc); |
| |
|
| | _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p)", |
| | static_cast<void *>(exception_object)); |
| | int frame_count = 0; |
| |
|
| | |
| | while (true) { |
| | |
| | |
| | |
| | |
| | _Unwind_State state = |
| | resume ? _US_UNWIND_FRAME_RESUME : _US_UNWIND_FRAME_STARTING; |
| | if (resume && frame_count == 1) { |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | __unw_set_reg(cursor, UNW_REG_IP, |
| | exception_object->unwinder_cache.reserved2); |
| | resume = false; |
| | } |
| |
|
| | |
| | unw_word_t sp; |
| | unw_proc_info_t frameInfo; |
| | __unw_get_reg(cursor, UNW_REG_SP, &sp); |
| | if (__unw_get_proc_info(cursor, &frameInfo) != UNW_ESUCCESS) { |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase2(ex_ojb=%p): __unw_get_proc_info " |
| | "failed => _URC_FATAL_PHASE2_ERROR", |
| | static_cast<void *>(exception_object)); |
| | return _URC_FATAL_PHASE2_ERROR; |
| | } |
| |
|
| | |
| | if (_LIBUNWIND_TRACING_UNWINDING) { |
| | char functionBuf[512]; |
| | const char *functionName = functionBuf; |
| | unw_word_t offset; |
| | if ((__unw_get_proc_name(cursor, functionBuf, sizeof(functionBuf), |
| | &offset) != UNW_ESUCCESS) || |
| | (frameInfo.start_ip + offset > frameInfo.end_ip)) |
| | functionName = ".anonymous."; |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase2(ex_ojb=%p): start_ip=0x%" PRIxPTR ", func=%s, sp=0x%" PRIxPTR ", " |
| | "lsda=0x%" PRIxPTR ", personality=0x%" PRIxPTR "", |
| | static_cast<void *>(exception_object), frameInfo.start_ip, |
| | functionName, sp, frameInfo.lsda, |
| | frameInfo.handler); |
| | } |
| |
|
| | |
| | if (frameInfo.handler != 0) { |
| | _Unwind_Personality_Fn p = |
| | (_Unwind_Personality_Fn)(long)(frameInfo.handler); |
| | struct _Unwind_Context *context = (struct _Unwind_Context *)(cursor); |
| | |
| | exception_object->pr_cache.fnstart = frameInfo.start_ip; |
| | exception_object->pr_cache.ehtp = |
| | (_Unwind_EHT_Header *)frameInfo.unwind_info; |
| | exception_object->pr_cache.additional = frameInfo.flags; |
| | _Unwind_Reason_Code personalityResult = |
| | (*p)(state, exception_object, context); |
| | switch (personalityResult) { |
| | case _URC_CONTINUE_UNWIND: |
| | |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND", |
| | static_cast<void *>(exception_object)); |
| | |
| | if (sp == exception_object->barrier_cache.sp) { |
| | |
| | _LIBUNWIND_ABORT("during phase1 personality function said it would " |
| | "stop here, but now in phase2 it did not stop here"); |
| | } |
| | break; |
| | case _URC_INSTALL_CONTEXT: |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT", |
| | static_cast<void *>(exception_object)); |
| | |
| | |
| | if (_LIBUNWIND_TRACING_UNWINDING) { |
| | unw_word_t pc; |
| | __unw_get_reg(cursor, UNW_REG_IP, &pc); |
| | __unw_get_reg(cursor, UNW_REG_SP, &sp); |
| | _LIBUNWIND_TRACE_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering " |
| | "user code with ip=0x%" PRIxPTR ", sp=0x%" PRIxPTR, |
| | static_cast<void *>(exception_object), |
| | pc, sp); |
| | } |
| |
|
| | { |
| | |
| | |
| | unw_word_t pc; |
| | __unw_get_reg(cursor, UNW_REG_IP, &pc); |
| | exception_object->unwinder_cache.reserved2 = (uint32_t)pc; |
| | } |
| | __unw_resume(cursor); |
| | |
| | return _URC_FATAL_PHASE2_ERROR; |
| |
|
| | |
| | case _URC_FAILURE: |
| | abort(); |
| |
|
| | default: |
| | |
| | _LIBUNWIND_DEBUG_LOG("personality function returned unknown result %d", |
| | personalityResult); |
| | return _URC_FATAL_PHASE2_ERROR; |
| | } |
| | } |
| | frame_count++; |
| | } |
| |
|
| | |
| | |
| | return _URC_FATAL_PHASE2_ERROR; |
| | } |
| |
|
| | |
| | _LIBUNWIND_EXPORT _Unwind_Reason_Code |
| | _Unwind_RaiseException(_Unwind_Exception *exception_object) { |
| | _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)", |
| | static_cast<void *>(exception_object)); |
| | unw_context_t uc; |
| | unw_cursor_t cursor; |
| | __unw_getcontext(&uc); |
| |
|
| | |
| | |
| | exception_object->unwinder_cache.reserved1 = 0; |
| |
|
| | |
| | _Unwind_Reason_Code phase1 = unwind_phase1(&uc, &cursor, exception_object); |
| | if (phase1 != _URC_NO_REASON) |
| | return phase1; |
| |
|
| | |
| | return unwind_phase2(&uc, &cursor, exception_object, false); |
| | } |
| |
|
| | _LIBUNWIND_EXPORT void _Unwind_Complete(_Unwind_Exception* exception_object) { |
| | |
| | |
| | (void)exception_object; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | _LIBUNWIND_EXPORT void |
| | _Unwind_Resume(_Unwind_Exception *exception_object) { |
| | _LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)", |
| | static_cast<void *>(exception_object)); |
| | unw_context_t uc; |
| | unw_cursor_t cursor; |
| | __unw_getcontext(&uc); |
| |
|
| | |
| | |
| | |
| | unwind_phase2(&uc, &cursor, exception_object, true); |
| |
|
| | |
| | _LIBUNWIND_ABORT("_Unwind_Resume() can't return"); |
| | } |
| |
|
| | |
| | _LIBUNWIND_EXPORT uintptr_t |
| | _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { |
| | unw_cursor_t *cursor = (unw_cursor_t *)context; |
| | unw_proc_info_t frameInfo; |
| | uintptr_t result = 0; |
| | if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) |
| | result = (uintptr_t)frameInfo.lsda; |
| | _LIBUNWIND_TRACE_API( |
| | "_Unwind_GetLanguageSpecificData(context=%p) => 0x%llx", |
| | static_cast<void *>(context), (long long)result); |
| | return result; |
| | } |
| |
|
| | static uint64_t ValueAsBitPattern(_Unwind_VRS_DataRepresentation representation, |
| | void* valuep) { |
| | uint64_t value = 0; |
| | switch (representation) { |
| | case _UVRSD_UINT32: |
| | case _UVRSD_FLOAT: |
| | memcpy(&value, valuep, sizeof(uint32_t)); |
| | break; |
| |
|
| | case _UVRSD_VFPX: |
| | case _UVRSD_UINT64: |
| | case _UVRSD_DOUBLE: |
| | memcpy(&value, valuep, sizeof(uint64_t)); |
| | break; |
| | } |
| | return value; |
| | } |
| |
|
| | _LIBUNWIND_EXPORT _Unwind_VRS_Result |
| | _Unwind_VRS_Set(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, |
| | uint32_t regno, _Unwind_VRS_DataRepresentation representation, |
| | void *valuep) { |
| | _LIBUNWIND_TRACE_API("_Unwind_VRS_Set(context=%p, regclass=%d, reg=%d, " |
| | "rep=%d, value=0x%llX)", |
| | static_cast<void *>(context), regclass, regno, |
| | representation, |
| | ValueAsBitPattern(representation, valuep)); |
| | unw_cursor_t *cursor = (unw_cursor_t *)context; |
| | switch (regclass) { |
| | case _UVRSC_CORE: |
| | if (representation != _UVRSD_UINT32 || regno > 15) |
| | return _UVRSR_FAILED; |
| | return __unw_set_reg(cursor, (unw_regnum_t)(UNW_ARM_R0 + regno), |
| | *(unw_word_t *)valuep) == UNW_ESUCCESS |
| | ? _UVRSR_OK |
| | : _UVRSR_FAILED; |
| | case _UVRSC_VFP: |
| | if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE) |
| | return _UVRSR_FAILED; |
| | if (representation == _UVRSD_VFPX) { |
| | |
| | if (regno > 15) |
| | return _UVRSR_FAILED; |
| | __unw_save_vfp_as_X(cursor); |
| | } else { |
| | if (regno > 31) |
| | return _UVRSR_FAILED; |
| | } |
| | return __unw_set_fpreg(cursor, (unw_regnum_t)(UNW_ARM_D0 + regno), |
| | *(unw_fpreg_t *)valuep) == UNW_ESUCCESS |
| | ? _UVRSR_OK |
| | : _UVRSR_FAILED; |
| | #if defined(__ARM_WMMX) |
| | case _UVRSC_WMMXC: |
| | if (representation != _UVRSD_UINT32 || regno > 3) |
| | return _UVRSR_FAILED; |
| | return __unw_set_reg(cursor, (unw_regnum_t)(UNW_ARM_WC0 + regno), |
| | *(unw_word_t *)valuep) == UNW_ESUCCESS |
| | ? _UVRSR_OK |
| | : _UVRSR_FAILED; |
| | case _UVRSC_WMMXD: |
| | if (representation != _UVRSD_DOUBLE || regno > 31) |
| | return _UVRSR_FAILED; |
| | return __unw_set_fpreg(cursor, (unw_regnum_t)(UNW_ARM_WR0 + regno), |
| | *(unw_fpreg_t *)valuep) == UNW_ESUCCESS |
| | ? _UVRSR_OK |
| | : _UVRSR_FAILED; |
| | #else |
| | case _UVRSC_WMMXC: |
| | case _UVRSC_WMMXD: |
| | break; |
| | #endif |
| | } |
| | _LIBUNWIND_ABORT("unsupported register class"); |
| | } |
| |
|
| | static _Unwind_VRS_Result |
| | _Unwind_VRS_Get_Internal(_Unwind_Context *context, |
| | _Unwind_VRS_RegClass regclass, uint32_t regno, |
| | _Unwind_VRS_DataRepresentation representation, |
| | void *valuep) { |
| | unw_cursor_t *cursor = (unw_cursor_t *)context; |
| | switch (regclass) { |
| | case _UVRSC_CORE: |
| | if (representation != _UVRSD_UINT32 || regno > 15) |
| | return _UVRSR_FAILED; |
| | return __unw_get_reg(cursor, (unw_regnum_t)(UNW_ARM_R0 + regno), |
| | (unw_word_t *)valuep) == UNW_ESUCCESS |
| | ? _UVRSR_OK |
| | : _UVRSR_FAILED; |
| | case _UVRSC_VFP: |
| | if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE) |
| | return _UVRSR_FAILED; |
| | if (representation == _UVRSD_VFPX) { |
| | |
| | if (regno > 15) |
| | return _UVRSR_FAILED; |
| | __unw_save_vfp_as_X(cursor); |
| | } else { |
| | if (regno > 31) |
| | return _UVRSR_FAILED; |
| | } |
| | return __unw_get_fpreg(cursor, (unw_regnum_t)(UNW_ARM_D0 + regno), |
| | (unw_fpreg_t *)valuep) == UNW_ESUCCESS |
| | ? _UVRSR_OK |
| | : _UVRSR_FAILED; |
| | #if defined(__ARM_WMMX) |
| | case _UVRSC_WMMXC: |
| | if (representation != _UVRSD_UINT32 || regno > 3) |
| | return _UVRSR_FAILED; |
| | return __unw_get_reg(cursor, (unw_regnum_t)(UNW_ARM_WC0 + regno), |
| | (unw_word_t *)valuep) == UNW_ESUCCESS |
| | ? _UVRSR_OK |
| | : _UVRSR_FAILED; |
| | case _UVRSC_WMMXD: |
| | if (representation != _UVRSD_DOUBLE || regno > 31) |
| | return _UVRSR_FAILED; |
| | return __unw_get_fpreg(cursor, (unw_regnum_t)(UNW_ARM_WR0 + regno), |
| | (unw_fpreg_t *)valuep) == UNW_ESUCCESS |
| | ? _UVRSR_OK |
| | : _UVRSR_FAILED; |
| | #else |
| | case _UVRSC_WMMXC: |
| | case _UVRSC_WMMXD: |
| | break; |
| | #endif |
| | } |
| | _LIBUNWIND_ABORT("unsupported register class"); |
| | } |
| |
|
| | _LIBUNWIND_EXPORT _Unwind_VRS_Result |
| | _Unwind_VRS_Get(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, |
| | uint32_t regno, _Unwind_VRS_DataRepresentation representation, |
| | void *valuep) { |
| | _Unwind_VRS_Result result = |
| | _Unwind_VRS_Get_Internal(context, regclass, regno, representation, |
| | valuep); |
| | _LIBUNWIND_TRACE_API("_Unwind_VRS_Get(context=%p, regclass=%d, reg=%d, " |
| | "rep=%d, value=0x%llX, result = %d)", |
| | static_cast<void *>(context), regclass, regno, |
| | representation, |
| | ValueAsBitPattern(representation, valuep), result); |
| | return result; |
| | } |
| |
|
| | _Unwind_VRS_Result |
| | _Unwind_VRS_Pop(_Unwind_Context *context, _Unwind_VRS_RegClass regclass, |
| | uint32_t discriminator, |
| | _Unwind_VRS_DataRepresentation representation) { |
| | _LIBUNWIND_TRACE_API("_Unwind_VRS_Pop(context=%p, regclass=%d, " |
| | "discriminator=%d, representation=%d)", |
| | static_cast<void *>(context), regclass, discriminator, |
| | representation); |
| | switch (regclass) { |
| | case _UVRSC_WMMXC: |
| | #if !defined(__ARM_WMMX) |
| | break; |
| | #endif |
| | case _UVRSC_CORE: { |
| | if (representation != _UVRSD_UINT32) |
| | return _UVRSR_FAILED; |
| | |
| | |
| | bool poppedSP = false; |
| | uint32_t* sp; |
| | if (_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, |
| | _UVRSD_UINT32, &sp) != _UVRSR_OK) { |
| | return _UVRSR_FAILED; |
| | } |
| | for (uint32_t i = 0; i < 16; ++i) { |
| | if (!(discriminator & static_cast<uint32_t>(1 << i))) |
| | continue; |
| | uint32_t value = *sp++; |
| | if (regclass == _UVRSC_CORE && i == 13) |
| | poppedSP = true; |
| | if (_Unwind_VRS_Set(context, regclass, i, |
| | _UVRSD_UINT32, &value) != _UVRSR_OK) { |
| | return _UVRSR_FAILED; |
| | } |
| | } |
| | if (!poppedSP) { |
| | return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, |
| | _UVRSD_UINT32, &sp); |
| | } |
| | return _UVRSR_OK; |
| | } |
| | case _UVRSC_WMMXD: |
| | #if !defined(__ARM_WMMX) |
| | break; |
| | #endif |
| | case _UVRSC_VFP: { |
| | if (representation != _UVRSD_VFPX && representation != _UVRSD_DOUBLE) |
| | return _UVRSR_FAILED; |
| | uint32_t first = discriminator >> 16; |
| | uint32_t count = discriminator & 0xffff; |
| | uint32_t end = first+count; |
| | uint32_t* sp; |
| | if (_Unwind_VRS_Get(context, _UVRSC_CORE, UNW_ARM_SP, |
| | _UVRSD_UINT32, &sp) != _UVRSR_OK) { |
| | return _UVRSR_FAILED; |
| | } |
| | |
| | |
| | for (uint32_t i = first; i < end; ++i) { |
| | |
| | uint64_t w0 = *sp++; |
| | uint64_t w1 = *sp++; |
| | #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ |
| | uint64_t value = (w1 << 32) | w0; |
| | #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ |
| | uint64_t value = (w0 << 32) | w1; |
| | #else |
| | #error "Unable to determine endianess" |
| | #endif |
| | if (_Unwind_VRS_Set(context, regclass, i, representation, &value) != |
| | _UVRSR_OK) |
| | return _UVRSR_FAILED; |
| | } |
| | if (representation == _UVRSD_VFPX) |
| | ++sp; |
| | return _Unwind_VRS_Set(context, _UVRSC_CORE, UNW_ARM_SP, _UVRSD_UINT32, |
| | &sp); |
| | } |
| | } |
| | _LIBUNWIND_ABORT("unsupported register class"); |
| | } |
| |
|
| | |
| | |
| | _LIBUNWIND_EXPORT uintptr_t |
| | _Unwind_GetRegionStart(struct _Unwind_Context *context) { |
| | unw_cursor_t *cursor = (unw_cursor_t *)context; |
| | unw_proc_info_t frameInfo; |
| | uintptr_t result = 0; |
| | if (__unw_get_proc_info(cursor, &frameInfo) == UNW_ESUCCESS) |
| | result = (uintptr_t)frameInfo.start_ip; |
| | _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%llX", |
| | static_cast<void *>(context), (long long)result); |
| | return result; |
| | } |
| |
|
| |
|
| | |
| | |
| | _LIBUNWIND_EXPORT void |
| | _Unwind_DeleteException(_Unwind_Exception *exception_object) { |
| | _LIBUNWIND_TRACE_API("_Unwind_DeleteException(ex_obj=%p)", |
| | static_cast<void *>(exception_object)); |
| | if (exception_object->exception_cleanup != NULL) |
| | (*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, |
| | exception_object); |
| | } |
| |
|
| | extern "C" _LIBUNWIND_EXPORT _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 |
| |
|