#include <stdint.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "libunwind.h"
#include "unwind.h"
#include "InternalMacros.h"
#if __ppc__ || __i386__ || __x86_64__
static _Unwind_Reason_Code unwind_phase1(unw_context_t* uc, struct _Unwind_Exception* exception_object)
{
unw_cursor_t cursor1;
unw_init_local(&cursor1, uc);
for (bool handlerNotFound = true; handlerNotFound; ) {
int stepResult = unw_step(&cursor1);
if ( stepResult == 0 ) {
DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object);
return _URC_END_OF_STACK;
}
else if ( stepResult < 0 ) {
DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): unw_step failed => _URC_FATAL_PHASE1_ERROR\n", exception_object);
return _URC_FATAL_PHASE1_ERROR;
}
unw_proc_info_t frameInfo;
unw_word_t sp;
if ( unw_get_proc_info(&cursor1, &frameInfo) != UNW_ESUCCESS ) {
DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): unw_get_proc_info failed => _URC_FATAL_PHASE1_ERROR\n", exception_object);
return _URC_FATAL_PHASE1_ERROR;
}
if ( DEBUG_PRINT_UNWINDING_TEST ) {
char functionName[512];
unw_word_t offset;
if ( (unw_get_proc_name(&cursor1, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip+offset > frameInfo.end_ip) )
strcpy(functionName, ".anonymous.");
unw_word_t pc;
unw_get_reg(&cursor1, UNW_REG_IP, &pc);
DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): pc=0x%llX, start_ip=0x%llX, func=%s, lsda=0x%llX, personality=0x%llX\n",
exception_object, pc, frameInfo.start_ip, functionName, frameInfo.lsda, frameInfo.handler);
}
if ( frameInfo.handler != 0 ) {
__personality_routine p = (__personality_routine)(long)(frameInfo.handler);
DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): calling personality function %p\n", exception_object, p);
_Unwind_Reason_Code personalityResult = (*p)(1, _UA_SEARCH_PHASE,
exception_object->exception_class, exception_object,
(struct _Unwind_Context*)(&cursor1));
switch ( personalityResult ) {
case _URC_HANDLER_FOUND:
handlerNotFound = false;
unw_get_reg(&cursor1, UNW_REG_SP, &sp);
exception_object->private_2 = sp;
DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_HANDLER_FOUND\n", exception_object);
return _URC_NO_REASON;
case _URC_CONTINUE_UNWIND:
DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object);
break;
default:
DEBUG_PRINT_UNWINDING("unwind_phase1(ex_ojb=%p): _URC_FATAL_PHASE1_ERROR\n", exception_object);
return _URC_FATAL_PHASE1_ERROR;
}
}
}
return _URC_NO_REASON;
}
static _Unwind_Reason_Code unwind_phase2(unw_context_t* uc, struct _Unwind_Exception* exception_object)
{
unw_cursor_t cursor2;
unw_init_local(&cursor2, uc);
DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p)\n", exception_object);
while ( true ) {
int stepResult = unw_step(&cursor2);
if ( stepResult == 0 ) {
DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step() reached bottom => _URC_END_OF_STACK\n", exception_object);
return _URC_END_OF_STACK;
}
else if ( stepResult < 0 ) {
DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_step failed => _URC_FATAL_PHASE1_ERROR\n", exception_object);
return _URC_FATAL_PHASE2_ERROR;
}
unw_word_t sp;
unw_proc_info_t frameInfo;
unw_get_reg(&cursor2, UNW_REG_SP, &sp);
if ( unw_get_proc_info(&cursor2, &frameInfo) != UNW_ESUCCESS ) {
DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): unw_get_proc_info failed => _URC_FATAL_PHASE1_ERROR\n", exception_object);
return _URC_FATAL_PHASE2_ERROR;
}
if ( DEBUG_PRINT_UNWINDING_TEST ) {
char functionName[512];
unw_word_t offset;
if ( (unw_get_proc_name(&cursor2, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip+offset > frameInfo.end_ip) )
strcpy(functionName, ".anonymous.");
DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): start_ip=0x%llX, func=%s, sp=0x%llX, lsda=0x%llX, personality=0x%llX\n",
exception_object, frameInfo.start_ip, functionName, sp, frameInfo.lsda, frameInfo.handler);
}
if ( frameInfo.handler != 0 ) {
__personality_routine p = (__personality_routine)(long)(frameInfo.handler);
_Unwind_Action action = _UA_CLEANUP_PHASE;
if ( sp == exception_object->private_2 )
action = (_Unwind_Action)(_UA_CLEANUP_PHASE|_UA_HANDLER_FRAME); _Unwind_Reason_Code personalityResult = (*p)(1, action,
exception_object->exception_class, exception_object,
(struct _Unwind_Context*)(&cursor2));
switch ( personalityResult ) {
case _URC_CONTINUE_UNWIND:
DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_CONTINUE_UNWIND\n", exception_object);
if ( sp == exception_object->private_2 ) {
ABORT("during phase1 personality function said it would stop here, but now if phase2 it did not stop here");
}
break;
case _URC_INSTALL_CONTEXT:
DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): _URC_INSTALL_CONTEXT\n", exception_object);
if ( DEBUG_PRINT_UNWINDING_TEST ) {
unw_word_t pc;
unw_word_t sp;
unw_get_reg(&cursor2, UNW_REG_IP, &pc);
unw_get_reg(&cursor2, UNW_REG_SP, &sp);
DEBUG_PRINT_UNWINDING("unwind_phase2(ex_ojb=%p): re-entering user code with ip=0x%llX, sp=0x%llX\n", exception_object, pc, sp);
}
unw_resume(&cursor2);
return _URC_FATAL_PHASE2_ERROR;
default:
DEBUG_MESSAGE("personality function returned unknown result %d", personalityResult);
return _URC_FATAL_PHASE2_ERROR;
}
}
}
return _URC_FATAL_PHASE2_ERROR;
}
static _Unwind_Reason_Code unwind_phase2_forced(unw_context_t* uc, struct _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 ) {
DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): unw_step failed => _URC_END_OF_STACK\n", exception_object);
return _URC_FATAL_PHASE1_ERROR;
}
if ( DEBUG_PRINT_UNWINDING_TEST ) {
char functionName[512];
unw_word_t offset;
if ( (unw_get_proc_name(&cursor2, functionName, 512, &offset) != UNW_ESUCCESS) || (frameInfo.start_ip+offset > frameInfo.end_ip) )
strcpy(functionName, ".anonymous.");
DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): start_ip=0x%llX, func=%s, lsda=0x%llX, personality=0x%llX\n",
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);
DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stop function returned %d\n", exception_object, stopResult);
if ( stopResult != _URC_NO_REASON ) {
DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): stopped by stop function\n", exception_object);
return _URC_FATAL_PHASE2_ERROR;
}
if ( frameInfo.handler != 0 ) {
__personality_routine p = (__personality_routine)(long)(frameInfo.handler);
DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling personality function %p\n", exception_object, p);
_Unwind_Reason_Code personalityResult = (*p)(1, action,
exception_object->exception_class, exception_object,
(struct _Unwind_Context*)(&cursor2));
switch ( personalityResult ) {
case _URC_CONTINUE_UNWIND:
DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_CONTINUE_UNWIND\n", exception_object);
break;
case _URC_INSTALL_CONTEXT:
DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned _URC_INSTALL_CONTEXT\n", exception_object);
unw_resume(&cursor2);
break;
default:
DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): personality returned %d, _URC_FATAL_PHASE2_ERROR\n",
exception_object, personalityResult);
return _URC_FATAL_PHASE2_ERROR;
}
}
}
DEBUG_PRINT_UNWINDING("unwind_phase2_forced(ex_ojb=%p): calling stop function with _UA_END_OF_STACK\n", 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;
}
EXPORT _Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception* exception_object)
{
DEBUG_PRINT_API("_Unwind_RaiseException(ex_obj=%p)\n", exception_object);
unw_context_t uc;
unw_getcontext(&uc);
exception_object->private_1 = 0;
exception_object->private_2 = 0;
_Unwind_Reason_Code phase1 = unwind_phase1(&uc, exception_object);
if ( phase1 != _URC_NO_REASON )
return phase1;
return unwind_phase2(&uc, exception_object);
}
EXPORT void _Unwind_Resume(struct _Unwind_Exception* exception_object)
{
DEBUG_PRINT_API("_Unwind_Resume(ex_obj=%p)\n", exception_object);
unw_context_t uc;
unw_getcontext(&uc);
if ( exception_object->private_1 != 0 )
unwind_phase2_forced(&uc, exception_object, (_Unwind_Stop_Fn)exception_object->private_1, (void*)exception_object->private_2);
else
unwind_phase2(&uc, exception_object);
ABORT("_Unwind_Resume() can't return");
}
EXPORT _Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception* exception_object, _Unwind_Stop_Fn stop, void* stop_parameter)
{
DEBUG_PRINT_API("_Unwind_ForcedUnwind(ex_obj=%p, stop=%p)\n", exception_object, stop);
unw_context_t uc;
unw_getcontext(&uc);
exception_object->private_1 = (uintptr_t)stop;
exception_object->private_2 = (uintptr_t)stop_parameter;
return unwind_phase2_forced(&uc, exception_object, stop, stop_parameter);
}
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 = frameInfo.lsda;
DEBUG_PRINT_API("_Unwind_GetLanguageSpecificData(context=%p) => 0x%lX\n", context, result);
if ( result != 0 ) {
if ( *((uint8_t*)result) != 0xFF )
DEBUG_MESSAGE("lsda at 0x%lX does not start with 0xFF\n", result);
}
return result;
}
EXPORT uintptr_t _Unwind_GetGR(struct _Unwind_Context* context, int index)
{
unw_cursor_t* cursor = (unw_cursor_t*)context;
unw_word_t result;
unw_get_reg(cursor, index, &result);
DEBUG_PRINT_API("_Unwind_GetGR(context=%p, reg=%d) => 0x%llX\n", context, index, (uint64_t)result);
return result;
}
EXPORT void _Unwind_SetGR(struct _Unwind_Context* context, int index, uintptr_t new_value)
{
DEBUG_PRINT_API("_Unwind_SetGR(context=%p, reg=%d, value=0x%0llX)\n", context, index, (uint64_t)new_value);
unw_cursor_t* cursor = (unw_cursor_t*)context;
unw_set_reg(cursor, index, new_value);
}
EXPORT uintptr_t _Unwind_GetIP(struct _Unwind_Context* context)
{
unw_cursor_t* cursor = (unw_cursor_t*)context;
unw_word_t result;
unw_get_reg(cursor, UNW_REG_IP, &result);
DEBUG_PRINT_API("_Unwind_GetIP(context=%p) => 0x%llX\n", context, (uint64_t)result);
return result;
}
EXPORT void _Unwind_SetIP(struct _Unwind_Context* context, uintptr_t new_value)
{
DEBUG_PRINT_API("_Unwind_SetIP(context=%p, value=0x%0llX)\n", context, (uint64_t)new_value);
unw_cursor_t* cursor = (unw_cursor_t*)context;
unw_set_reg(cursor, UNW_REG_IP, new_value);
}
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 = frameInfo.start_ip;
DEBUG_PRINT_API("_Unwind_GetRegionStart(context=%p) => 0x%lX\n", context, result);
return result;
}
EXPORT void _Unwind_DeleteException(struct _Unwind_Exception* exception_object)
{
DEBUG_PRINT_API("_Unwind_DeleteException(ex_obj=%p)\n", exception_object);
if ( exception_object->exception_cleanup != NULL )
(*exception_object->exception_cleanup)(_URC_FOREIGN_EXCEPTION_CAUGHT, exception_object);
}
NOT_HERE_BEFORE_10_6(_Unwind_DeleteException)
NOT_HERE_BEFORE_10_6(_Unwind_Find_FDE)
NOT_HERE_BEFORE_10_6(_Unwind_ForcedUnwind)
NOT_HERE_BEFORE_10_6(_Unwind_GetGR)
NOT_HERE_BEFORE_10_6(_Unwind_GetIP)
NOT_HERE_BEFORE_10_6(_Unwind_GetLanguageSpecificData)
NOT_HERE_BEFORE_10_6(_Unwind_GetRegionStart)
NOT_HERE_BEFORE_10_6(_Unwind_RaiseException)
NOT_HERE_BEFORE_10_6(_Unwind_Resume)
NOT_HERE_BEFORE_10_6(_Unwind_SetGR)
NOT_HERE_BEFORE_10_6(_Unwind_SetIP)
#endif // __ppc__ || __i386__ || __x86_64__