| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | #include "config.h" |
| |
|
| | #if defined(_LIBUNWIND_SUPPORT_SEH_UNWIND) |
| |
|
| | #include <unwind.h> |
| |
|
| | #include <stdint.h> |
| | #include <stdbool.h> |
| | #include <stdlib.h> |
| |
|
| | #include <windef.h> |
| | #include <excpt.h> |
| | #include <winnt.h> |
| | #include <ntstatus.h> |
| |
|
| | #include "libunwind_ext.h" |
| | #include "UnwindCursor.hpp" |
| |
|
| | using namespace libunwind; |
| |
|
| | #define STATUS_USER_DEFINED (1u << 29) |
| |
|
| | #define STATUS_GCC_MAGIC (('G' << 16) | ('C' << 8) | 'C') |
| |
|
| | #define MAKE_CUSTOM_STATUS(s, c) \ |
| | ((NTSTATUS)(((s) << 30) | STATUS_USER_DEFINED | (c))) |
| | #define MAKE_GCC_EXCEPTION(c) \ |
| | MAKE_CUSTOM_STATUS(STATUS_SEVERITY_SUCCESS, STATUS_GCC_MAGIC | ((c) << 24)) |
| |
|
| | |
| | |
| | #define STATUS_GCC_THROW MAKE_GCC_EXCEPTION(0) |
| | |
| | |
| | #define STATUS_GCC_UNWIND MAKE_GCC_EXCEPTION(1) |
| |
|
| | |
| | static const uint64_t kSEHExceptionClass = 0x434C4E4753454800; |
| |
|
| | |
| | |
| | static void seh_exc_cleanup(_Unwind_Reason_Code urc, _Unwind_Exception *exc) { |
| | (void)urc; |
| | if (exc->exception_class != kSEHExceptionClass) |
| | _LIBUNWIND_ABORT("SEH cleanup called on non-SEH exception"); |
| | free(exc); |
| | } |
| |
|
| | static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *ctx); |
| | static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor); |
| | static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor, |
| | DISPATCHER_CONTEXT *disp); |
| |
|
| | |
| | |
| | |
| | |
| | _LIBUNWIND_EXPORT EXCEPTION_DISPOSITION |
| | _GCC_specific_handler(PEXCEPTION_RECORD ms_exc, PVOID frame, PCONTEXT ms_ctx, |
| | DISPATCHER_CONTEXT *disp, _Unwind_Personality_Fn pers) { |
| | unw_cursor_t cursor; |
| | _Unwind_Exception *exc; |
| | _Unwind_Action action; |
| | struct _Unwind_Context *ctx = nullptr; |
| | _Unwind_Reason_Code urc; |
| | uintptr_t retval, target; |
| | bool ours = false; |
| |
|
| | _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler(%#010lx(%lx), %p)", |
| | ms_exc->ExceptionCode, ms_exc->ExceptionFlags, |
| | (void *)frame); |
| | if (ms_exc->ExceptionCode == STATUS_GCC_UNWIND) { |
| | if (IS_TARGET_UNWIND(ms_exc->ExceptionFlags)) { |
| | |
| | |
| | #ifdef __x86_64__ |
| | disp->ContextRecord->Rdx = ms_exc->ExceptionInformation[3]; |
| | #elif defined(__arm__) |
| | disp->ContextRecord->R1 = ms_exc->ExceptionInformation[3]; |
| | #elif defined(__aarch64__) |
| | disp->ContextRecord->X1 = ms_exc->ExceptionInformation[3]; |
| | #endif |
| | } |
| | |
| | return ExceptionContinueSearch; |
| | } |
| |
|
| | if (ms_exc->ExceptionCode == STATUS_GCC_THROW) { |
| | |
| | |
| | ours = true; |
| | exc = (_Unwind_Exception *)ms_exc->ExceptionInformation[0]; |
| | if (!IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1) { |
| | ctx = (struct _Unwind_Context *)ms_exc->ExceptionInformation[1]; |
| | action = (_Unwind_Action)ms_exc->ExceptionInformation[2]; |
| | } |
| | } else { |
| | |
| | exc = (_Unwind_Exception *)malloc(sizeof(_Unwind_Exception)); |
| | exc->exception_class = kSEHExceptionClass; |
| | exc->exception_cleanup = seh_exc_cleanup; |
| | memset(exc->private_, 0, sizeof(exc->private_)); |
| | } |
| | if (!ctx) { |
| | __unw_init_seh(&cursor, disp->ContextRecord); |
| | __unw_seh_set_disp_ctx(&cursor, disp); |
| | __unw_set_reg(&cursor, UNW_REG_IP, disp->ControlPc - 1); |
| | ctx = (struct _Unwind_Context *)&cursor; |
| |
|
| | if (!IS_UNWINDING(ms_exc->ExceptionFlags)) { |
| | if (ours && ms_exc->NumberParameters > 1) |
| | action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_FORCE_UNWIND); |
| | else |
| | action = _UA_SEARCH_PHASE; |
| | } else { |
| | if (ours && ms_exc->ExceptionInformation[1] == (ULONG_PTR)frame) |
| | action = (_Unwind_Action)(_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME); |
| | else |
| | action = _UA_CLEANUP_PHASE; |
| | } |
| | } |
| |
|
| | _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() calling personality " |
| | "function %p(1, %d, %llx, %p, %p)", |
| | (void *)pers, action, exc->exception_class, |
| | (void *)exc, (void *)ctx); |
| | urc = pers(1, action, exc->exception_class, exc, ctx); |
| | _LIBUNWIND_TRACE_UNWINDING("_GCC_specific_handler() personality returned %d", urc); |
| | switch (urc) { |
| | case _URC_CONTINUE_UNWIND: |
| | |
| | |
| | if (action & _UA_HANDLER_FRAME) |
| | _LIBUNWIND_ABORT("Personality continued unwind at the target frame!"); |
| | return ExceptionContinueSearch; |
| | case _URC_HANDLER_FOUND: |
| | |
| | |
| | if (ours && ms_exc->NumberParameters > 1) |
| | return 4 ; |
| | |
| | if (IS_UNWINDING(ms_exc->ExceptionFlags)) |
| | _LIBUNWIND_ABORT("Personality indicated exception handler in phase 2!"); |
| | exc->private_[1] = (ULONG_PTR)frame; |
| | if (ours) { |
| | ms_exc->NumberParameters = 4; |
| | ms_exc->ExceptionInformation[1] = (ULONG_PTR)frame; |
| | } |
| | |
| | |
| | RtlUnwindEx(frame, (PVOID)disp->ControlPc, ms_exc, exc, ms_ctx, disp->HistoryTable); |
| | _LIBUNWIND_ABORT("RtlUnwindEx() failed"); |
| | case _URC_INSTALL_CONTEXT: { |
| | |
| | |
| | |
| | if (ours && !IS_UNWINDING(ms_exc->ExceptionFlags) && ms_exc->NumberParameters > 1) |
| | return 4 ; |
| | |
| | if (!IS_UNWINDING(ms_exc->ExceptionFlags)) |
| | _LIBUNWIND_ABORT("Personality installed context during phase 1!"); |
| | #ifdef __x86_64__ |
| | exc->private_[2] = disp->TargetIp; |
| | __unw_get_reg(&cursor, UNW_X86_64_RAX, &retval); |
| | __unw_get_reg(&cursor, UNW_X86_64_RDX, &exc->private_[3]); |
| | #elif defined(__arm__) |
| | exc->private_[2] = disp->TargetPc; |
| | __unw_get_reg(&cursor, UNW_ARM_R0, &retval); |
| | __unw_get_reg(&cursor, UNW_ARM_R1, &exc->private_[3]); |
| | #elif defined(__aarch64__) |
| | exc->private_[2] = disp->TargetPc; |
| | __unw_get_reg(&cursor, UNW_ARM64_X0, &retval); |
| | __unw_get_reg(&cursor, UNW_ARM64_X1, &exc->private_[3]); |
| | #endif |
| | __unw_get_reg(&cursor, UNW_REG_IP, &target); |
| | ms_exc->ExceptionCode = STATUS_GCC_UNWIND; |
| | #ifdef __x86_64__ |
| | ms_exc->ExceptionInformation[2] = disp->TargetIp; |
| | #elif defined(__arm__) || defined(__aarch64__) |
| | ms_exc->ExceptionInformation[2] = disp->TargetPc; |
| | #endif |
| | ms_exc->ExceptionInformation[3] = exc->private_[3]; |
| | |
| | |
| | |
| | CONTEXT new_ctx; |
| | RtlUnwindEx(frame, (PVOID)target, ms_exc, (PVOID)retval, &new_ctx, disp->HistoryTable); |
| | _LIBUNWIND_ABORT("RtlUnwindEx() failed"); |
| | } |
| | |
| | default: return ExceptionContinueExecution; |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | extern "C" _Unwind_Reason_Code |
| | __libunwind_seh_personality(int version, _Unwind_Action state, |
| | uint64_t klass, _Unwind_Exception *exc, |
| | struct _Unwind_Context *context) { |
| | (void)version; |
| | (void)klass; |
| | EXCEPTION_RECORD ms_exc; |
| | bool phase2 = (state & (_UA_SEARCH_PHASE|_UA_CLEANUP_PHASE)) == _UA_CLEANUP_PHASE; |
| | ms_exc.ExceptionCode = STATUS_GCC_THROW; |
| | ms_exc.ExceptionFlags = 0; |
| | ms_exc.NumberParameters = 3; |
| | ms_exc.ExceptionInformation[0] = (ULONG_PTR)exc; |
| | ms_exc.ExceptionInformation[1] = (ULONG_PTR)context; |
| | ms_exc.ExceptionInformation[2] = state; |
| | DISPATCHER_CONTEXT *disp_ctx = |
| | __unw_seh_get_disp_ctx((unw_cursor_t *)context); |
| | EXCEPTION_DISPOSITION ms_act = disp_ctx->LanguageHandler(&ms_exc, |
| | (PVOID)disp_ctx->EstablisherFrame, |
| | disp_ctx->ContextRecord, |
| | disp_ctx); |
| | switch (ms_act) { |
| | case ExceptionContinueSearch: return _URC_CONTINUE_UNWIND; |
| | case 4 : |
| | return phase2 ? _URC_INSTALL_CONTEXT : _URC_HANDLER_FOUND; |
| | default: |
| | return phase2 ? _URC_FATAL_PHASE2_ERROR : _URC_FATAL_PHASE1_ERROR; |
| | } |
| | } |
| |
|
| | static _Unwind_Reason_Code |
| | unwind_phase2_forced(unw_context_t *uc, |
| | _Unwind_Exception *exception_object, |
| | _Unwind_Stop_Fn stop, void *stop_parameter) { |
| | unw_cursor_t cursor2; |
| | __unw_init_local(&cursor2, uc); |
| |
|
| | |
| | while (__unw_step(&cursor2) > 0) { |
| |
|
| | |
| | unw_proc_info_t frameInfo; |
| | if (__unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS) { |
| | _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): __unw_step " |
| | "failed => _URC_END_OF_STACK", |
| | (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(&cursor2, functionBuf, sizeof(functionBuf), |
| | &offset) != UNW_ESUCCESS) || |
| | (frameInfo.start_ip + offset > frameInfo.end_ip)) |
| | functionName = ".anonymous."; |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase2_forced(ex_ojb=%p): start_ip=0x%" PRIx64 |
| | ", func=%s, lsda=0x%" PRIx64 ", personality=0x%" PRIx64, |
| | (void *)exception_object, frameInfo.start_ip, functionName, |
| | frameInfo.lsda, frameInfo.handler); |
| | } |
| |
|
| | |
| | _Unwind_Action action = |
| | (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE); |
| | _Unwind_Reason_Code stopResult = |
| | (*stop)(1, action, exception_object->exception_class, exception_object, |
| | (struct _Unwind_Context *)(&cursor2), stop_parameter); |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase2_forced(ex_ojb=%p): stop function returned %d", |
| | (void *)exception_object, stopResult); |
| | if (stopResult != _URC_NO_REASON) { |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase2_forced(ex_ojb=%p): stopped by stop function", |
| | (void *)exception_object); |
| | return _URC_FATAL_PHASE2_ERROR; |
| | } |
| |
|
| | |
| | if (frameInfo.handler != 0) { |
| | _Unwind_Personality_Fn p = |
| | (_Unwind_Personality_Fn)(intptr_t)(frameInfo.handler); |
| | _LIBUNWIND_TRACE_UNWINDING( |
| | "unwind_phase2_forced(ex_ojb=%p): calling personality function %p", |
| | (void *)exception_object, (void *)(uintptr_t)p); |
| | _Unwind_Reason_Code personalityResult = |
| | (*p)(1, action, exception_object->exception_class, exception_object, |
| | (struct _Unwind_Context *)(&cursor2)); |
| | switch (personalityResult) { |
| | case _URC_CONTINUE_UNWIND: |
| | _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " |
| | "personality returned " |
| | "_URC_CONTINUE_UNWIND", |
| | (void *)exception_object); |
| | |
| | break; |
| | case _URC_INSTALL_CONTEXT: |
| | _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " |
| | "personality returned " |
| | "_URC_INSTALL_CONTEXT", |
| | (void *)exception_object); |
| | |
| | __unw_resume(&cursor2); |
| | break; |
| | default: |
| | |
| | _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): " |
| | "personality returned %d, " |
| | "_URC_FATAL_PHASE2_ERROR", |
| | (void *)exception_object, personalityResult); |
| | return _URC_FATAL_PHASE2_ERROR; |
| | } |
| | } |
| | } |
| |
|
| | |
| | |
| | _LIBUNWIND_TRACE_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop " |
| | "function with _UA_END_OF_STACK", |
| | (void *)exception_object); |
| | _Unwind_Action lastAction = |
| | (_Unwind_Action)(_UA_FORCE_UNWIND | _UA_CLEANUP_PHASE | _UA_END_OF_STACK); |
| | (*stop)(1, lastAction, exception_object->exception_class, exception_object, |
| | (struct _Unwind_Context *)(&cursor2), stop_parameter); |
| |
|
| | |
| | |
| | return _URC_FATAL_PHASE2_ERROR; |
| | } |
| |
|
| | |
| | _LIBUNWIND_EXPORT _Unwind_Reason_Code |
| | _Unwind_RaiseException(_Unwind_Exception *exception_object) { |
| | _LIBUNWIND_TRACE_API("_Unwind_RaiseException(ex_obj=%p)", |
| | (void *)exception_object); |
| |
|
| | |
| | |
| | memset(exception_object->private_, 0, sizeof(exception_object->private_)); |
| |
|
| | |
| | |
| | RaiseException(STATUS_GCC_THROW, 0, 1, (ULONG_PTR *)&exception_object); |
| |
|
| | |
| | |
| | return _URC_END_OF_STACK; |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | _LIBUNWIND_EXPORT void |
| | _Unwind_Resume(_Unwind_Exception *exception_object) { |
| | _LIBUNWIND_TRACE_API("_Unwind_Resume(ex_obj=%p)", (void *)exception_object); |
| |
|
| | if (exception_object->private_[0] != 0) { |
| | unw_context_t uc; |
| |
|
| | __unw_getcontext(&uc); |
| | unwind_phase2_forced(&uc, exception_object, |
| | (_Unwind_Stop_Fn) exception_object->private_[0], |
| | (void *)exception_object->private_[4]); |
| | } else { |
| | |
| | |
| | EXCEPTION_RECORD ms_exc; |
| | CONTEXT ms_ctx; |
| | UNWIND_HISTORY_TABLE hist; |
| |
|
| | memset(&ms_exc, 0, sizeof(ms_exc)); |
| | memset(&hist, 0, sizeof(hist)); |
| | ms_exc.ExceptionCode = STATUS_GCC_THROW; |
| | ms_exc.ExceptionFlags = EXCEPTION_NONCONTINUABLE; |
| | ms_exc.NumberParameters = 4; |
| | ms_exc.ExceptionInformation[0] = (ULONG_PTR)exception_object; |
| | ms_exc.ExceptionInformation[1] = exception_object->private_[1]; |
| | ms_exc.ExceptionInformation[2] = exception_object->private_[2]; |
| | ms_exc.ExceptionInformation[3] = exception_object->private_[3]; |
| | RtlUnwindEx((PVOID)exception_object->private_[1], |
| | (PVOID)exception_object->private_[2], &ms_exc, |
| | exception_object, &ms_ctx, &hist); |
| | } |
| |
|
| | |
| | _LIBUNWIND_ABORT("_Unwind_Resume() can't return"); |
| | } |
| |
|
| | |
| | |
| | |
| | _LIBUNWIND_EXPORT _Unwind_Reason_Code |
| | _Unwind_ForcedUnwind(_Unwind_Exception *exception_object, |
| | _Unwind_Stop_Fn stop, void *stop_parameter) { |
| | _LIBUNWIND_TRACE_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)", |
| | (void *)exception_object, (void *)(uintptr_t)stop); |
| | unw_context_t uc; |
| | __unw_getcontext(&uc); |
| |
|
| | |
| | |
| | exception_object->private_[0] = (uintptr_t) stop; |
| | exception_object->private_[4] = (uintptr_t) stop_parameter; |
| |
|
| | |
| | return unwind_phase2_forced(&uc, exception_object, stop, stop_parameter); |
| | } |
| |
|
| | |
| | _LIBUNWIND_EXPORT uintptr_t |
| | _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context) { |
| | uintptr_t result = |
| | (uintptr_t)__unw_seh_get_disp_ctx((unw_cursor_t *)context)->HandlerData; |
| | _LIBUNWIND_TRACE_API( |
| | "_Unwind_GetLanguageSpecificData(context=%p) => 0x%" PRIxPTR, |
| | (void *)context, result); |
| | return result; |
| | } |
| |
|
| | |
| | |
| | _LIBUNWIND_EXPORT uintptr_t |
| | _Unwind_GetRegionStart(struct _Unwind_Context *context) { |
| | DISPATCHER_CONTEXT *disp = __unw_seh_get_disp_ctx((unw_cursor_t *)context); |
| | uintptr_t result = (uintptr_t)disp->FunctionEntry->BeginAddress + disp->ImageBase; |
| | _LIBUNWIND_TRACE_API("_Unwind_GetRegionStart(context=%p) => 0x%" PRIxPTR, |
| | (void *)context, result); |
| | return result; |
| | } |
| |
|
| | static int __unw_init_seh(unw_cursor_t *cursor, CONTEXT *context) { |
| | #ifdef _LIBUNWIND_TARGET_X86_64 |
| | new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor)) |
| | UnwindCursor<LocalAddressSpace, Registers_x86_64>( |
| | context, LocalAddressSpace::sThisAddressSpace); |
| | auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor); |
| | co->setInfoBasedOnIPRegister(); |
| | return UNW_ESUCCESS; |
| | #elif defined(_LIBUNWIND_TARGET_ARM) |
| | new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor)) |
| | UnwindCursor<LocalAddressSpace, Registers_arm>( |
| | context, LocalAddressSpace::sThisAddressSpace); |
| | auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor); |
| | co->setInfoBasedOnIPRegister(); |
| | return UNW_ESUCCESS; |
| | #elif defined(_LIBUNWIND_TARGET_AARCH64) |
| | new (reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor)) |
| | UnwindCursor<LocalAddressSpace, Registers_arm64>( |
| | context, LocalAddressSpace::sThisAddressSpace); |
| | auto *co = reinterpret_cast<AbstractUnwindCursor *>(cursor); |
| | co->setInfoBasedOnIPRegister(); |
| | return UNW_ESUCCESS; |
| | #else |
| | return UNW_EINVAL; |
| | #endif |
| | } |
| |
|
| | static DISPATCHER_CONTEXT *__unw_seh_get_disp_ctx(unw_cursor_t *cursor) { |
| | #ifdef _LIBUNWIND_TARGET_X86_64 |
| | return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor)->getDispatcherContext(); |
| | #elif defined(_LIBUNWIND_TARGET_ARM) |
| | return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor)->getDispatcherContext(); |
| | #elif defined(_LIBUNWIND_TARGET_AARCH64) |
| | return reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor)->getDispatcherContext(); |
| | #else |
| | return nullptr; |
| | #endif |
| | } |
| |
|
| | static void __unw_seh_set_disp_ctx(unw_cursor_t *cursor, |
| | DISPATCHER_CONTEXT *disp) { |
| | #ifdef _LIBUNWIND_TARGET_X86_64 |
| | reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_x86_64> *>(cursor)->setDispatcherContext(disp); |
| | #elif defined(_LIBUNWIND_TARGET_ARM) |
| | reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm> *>(cursor)->setDispatcherContext(disp); |
| | #elif defined(_LIBUNWIND_TARGET_AARCH64) |
| | reinterpret_cast<UnwindCursor<LocalAddressSpace, Registers_arm64> *>(cursor)->setDispatcherContext(disp); |
| | #endif |
| | } |
| |
|
| | #endif |
| |
|