X-Git-Url: https://git.ucc.asn.au/?p=tpg%2Facess2.git;a=blobdiff_plain;f=Usermode%2FLibraries%2Flibc%2B%2B.so_src%2Fgxx_personality.cc;fp=Usermode%2FLibraries%2Flibc%2B%2B.so_src%2Fgxx_personality.cc;h=fbd1623417db6e03d193a7174659bc8add90a415;hp=0000000000000000000000000000000000000000;hb=845b6f9d90bb87b5e760e4d49aa93b0e003ab750;hpb=67a7fe2bb79eceaf10c572a99bd8345c4e81cf5b diff --git a/Usermode/Libraries/libc++.so_src/gxx_personality.cc b/Usermode/Libraries/libc++.so_src/gxx_personality.cc new file mode 100644 index 00000000..fbd16234 --- /dev/null +++ b/Usermode/Libraries/libc++.so_src/gxx_personality.cc @@ -0,0 +1,461 @@ +/* + * Acess2 C++ Support Library + * - By John Hodge (thePowersGang) + * + * gxx_personality.cc + * - Implementation of GNU C++ Exception handling "Personality" + */ +#include +#include // [u]intN_t +#include // abort +#include // memcmp +#include "exception_handling_acxx.h" +#include "exception_handling_cxxabi.h" +#include +#include +#include // __dynamic_cast + +#define DEBUG_ENABLED 1 + +#if DEBUG_ENABLED +# define DEBUG(v...) ::_SysDebug(v) +#else +# define DEBUG(v...) do{}while(0) +#endif + +// === PROTOTYPES === +extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass, + struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context); +static int get_frame_action(const sLSDA_Header &header, _Unwind_Context *context, const std::type_info *throw_type, + uint64_t &landingpad, int64_t &switch_value); +static bool exception_matches_single(const std::type_info *throw_type, const struct sLSDA_Header &header, int type_index); +static bool exception_matches_list(const std::type_info *throw_type, const struct sLSDA_Header &header, int list_index); +static void read_lsda_header(const void *&ptr, _Unwind_Context *context, struct sLSDA_Header *header); +static size_t _get_encoded_size(int encoding); +static uint64_t _get_base(uint8_t encoding, _Unwind_Context *context); +static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding, uint64_t base); +static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding); +template static T read(const void *&ptr); +static uint64_t _read_leb128u(const void *&ptr); +static int64_t _read_leb128s(const void *&ptr); + +// === CODE === +extern "C" _Unwind_Reason_Code __gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass, + struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context) +{ + //::_SysDebug("__gxx_personality_v0(%i, 0x%x, 0x%llx, ...)", version, actions, exceptionClass); + //::_SysDebug("__gxx_personality_v0(..., %p, %p)", exceptionObject, context); + + //if( exceptionObject ) { + // ::_SysDebug("exception_object: Class 0x%llx", exceptionObject->exception_class); + //} + //if( context ) + //{ + // ::_SysDebug("context: IP 0x%llx", _Unwind_GetIP(context)); + // ::_SysDebug("context: RegionStart 0x%llx", _Unwind_GetRegionStart(context)); + // ::_SysDebug("context: LangSpecData 0x%llx", _Unwind_GetLanguageSpecificData(context)); + //} + + if( version != 1 ) { + ::_SysDebug("%s: Unexpected version %i != exp 1", __func__, version); + return _URC_FATAL_PHASE1_ERROR; + } + + const void *lsda_ptr = (const void*)_Unwind_GetLanguageSpecificData(context); + struct sLSDA_Header header; + read_lsda_header(lsda_ptr, context, &header); + + const void *exception_object = exceptionObject + 1; + const __cxa_exception *exception_info = static_cast(exception_object) - 1; + std::type_info *throw_type = ( + memcmp(&exceptionClass,EXCEPTION_CLASS_ACESS,8) == 0 ? exception_info->exceptionType : NULL + ); + + uint64_t landingpad = 0; + int64_t switch_value = 0; + int frame_action = get_frame_action(header, context, throw_type, landingpad, switch_value); + + if( (actions & _UA_SEARCH_PHASE) && (actions & _UA_CLEANUP_PHASE) ) + { + _SysDebug("__gxx_personality_v0: ERROR Both _UA_SEARCH_PHASE and _UA_CLEANUP_PHASE set"); + abort(); + } + + // Search + if( actions & _UA_SEARCH_PHASE ) + { + // If a handler was found + if( frame_action == 2 ) { + // - return _URC_HANDLER_FOUND + DEBUG("SEARCH: 0x%llx Handler %p(%i)", + _Unwind_GetIP(context), landingpad, switch_value); + return _URC_HANDLER_FOUND; + } + // - If no handler (either nothing, or cleanups), return _URC_CONTINUE_UNWIND + DEBUG("SEARCH: 0x%llx no handler %p(%i)", + _Unwind_GetIP(context), landingpad, switch_value); + return _URC_CONTINUE_UNWIND; + } + + // Cleanup + if( actions & _UA_CLEANUP_PHASE ) + { + // No action, continue + if( frame_action == 0 ) { + return _URC_CONTINUE_UNWIND; + } + assert(landingpad); + + if( actions & _UA_FORCE_UNWIND ) + { + // Unwind forced, override switch_value to 0 + // - Disables any attempt to handle exception + switch_value = 0; + } + + DEBUG("Install context IP=0x%x, R%i=%p/R%i=%i", + (uintptr_t)landingpad, + __builtin_eh_return_data_regno(0), exceptionObject, + __builtin_eh_return_data_regno(1), switch_value + ); + _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)exceptionObject); + _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), switch_value); + _Unwind_SetIP(context, landingpad); + return _URC_INSTALL_CONTEXT; + } + + _SysDebug("__gxx_personality_v0: Neither _UA_SEARCH_PHASE or _UA_CLEANUP_PHASE set"); + return _URC_FATAL_PHASE1_ERROR; +} + +int get_frame_action(const sLSDA_Header &header, _Unwind_Context *context, const std::type_info* throw_type, + uint64_t &landingpad, int64_t &switch_value) +{ + uint64_t ip = _Unwind_GetIP(context) - _Unwind_GetRegionStart(context); + DEBUG("get_frame_action: IP = 0x%llx + 0x%llx", _Unwind_GetRegionStart(context), ip); + // Check if there is a handler for this exception in this frame + // - Search call site table for this return address (corresponds to a try block) + uintptr_t cs_ldgpad; + uint64_t cs_action; + const void *lsda_ptr = header.CallSiteTable; + while( lsda_ptr < header.ActionTable ) + { + uintptr_t cs_start = _read_encoded(lsda_ptr, context, header.CallSiteEncoding); + uintptr_t cs_len = _read_encoded(lsda_ptr, context, header.CallSiteEncoding); + cs_ldgpad = _read_encoded(lsda_ptr, context, header.CallSiteEncoding); + cs_action = _read_leb128u(lsda_ptr); + + // If IP falls below this entry, it's not on the list + if( ip <= cs_start ) { + lsda_ptr = header.ActionTable; + break ; + } + if( ip > cs_start + cs_len ) + continue; + + break; + } + if( lsda_ptr > header.ActionTable ) { + _SysDebug("__gxx_personality_v0: Malformed Call Site Table, reading overran the end (%p>%p)", + lsda_ptr, header.ActionTable); + } + if( lsda_ptr >= header.ActionTable ) { + // No match! + DEBUG("__gxx_personality_v0: No entry for IP 0x%x", ip); + return 0; + } + + // Found it + if( cs_ldgpad == 0 ) { + DEBUG("No landingpad, hence no action"); + if( cs_action != 0 ) { + _SysDebug("%s: NOTICE cs_ldgpad==0 but cs_action(0x%llx)!=0", + __func__, cs_action); + } + return 0; + } + else if( cs_action == 0 ) { + DEBUG("No action, cleanups only"); + switch_value = 0; + landingpad = header.LPStart + cs_ldgpad; + return 1; // 1 = cleanup only + } + else { + switch_value = 0; + landingpad = header.LPStart + cs_ldgpad; + // catch() handler + bool has_cleanups = false; // set to true to override return value + const void *action_list = (const char*)header.ActionTable + (cs_action-1); + for(;;) // broken by 0 length entry + { + leb128s_t filter = _read_leb128s(action_list); + leb128s_t disp = _read_leb128s(action_list); + if( filter == 0 ) + { + // Cleanup + has_cleanups = true; + } + else if( filter < 0 ) + { + // Exception specifcation + if( !exception_matches_list(throw_type, header, -filter) ) + { + switch_value = filter; + return 2; + } + } + else + { + // Catch + if( exception_matches_single(throw_type, header, filter) ) + { + switch_value = filter; + return 2; + } + } + + if( disp == 0 ) + break; + action_list = (const char*)action_list + disp-1; + } + + // If a cleanup request was seen, tell the caller that we want to handle it + if( has_cleanups ) { + return 1; // 1 = cleanup only + } + // Else, there's nothing here to handle + return 0; + } +} + +const ::std::type_info *get_type_info(const struct sLSDA_Header &header, int type_index) +{ + size_t encoded_size = _get_encoded_size(header.TTEncoding); + assert(encoded_size > 0); + const void *ptr = (const void*)(header.TTBase - encoded_size * type_index); + assert( header.TTBase ); + assert( ptr > header.ActionTable ); + + uintptr_t type_ptr = _read_encoded(ptr, NULL, header.TTEncoding, header.TypePtrBase); + return reinterpret_cast< ::std::type_info* >(type_ptr); +} + +const ::std::type_info *get_exception_type(const void *exception_object) +{ + if( !exception_object ) + return NULL; + const struct _Unwind_Exception *u_execept = (const struct _Unwind_Exception*)exception_object; + const struct __cxa_exception *except = (const struct __cxa_exception*)(u_execept + 1) - 1; + if( memcmp(&except->unwindHeader.exception_class, EXCEPTION_CLASS_ACESS, 8) != 0 ) + return NULL; + + return except->exceptionType; +} + +bool exception_matches_single(const std::type_info *throw_type, const struct sLSDA_Header &header, int type_index) +{ + const ::std::type_info *catch_type = get_type_info(header, type_index); + DEBUG("catch_type = %p", catch_type); + + if( !catch_type ) + { + DEBUG("catch(...)"); + return true; + } + else if( !throw_type ) + { + DEBUG("threw UNK"); + return false; + } + else + { + DEBUG("catch(%s), throw %s", catch_type->name(), throw_type->name()); + size_t ofs = 0; + if( !catch_type->__is_child(*throw_type, ofs) ) { + _SysDebug("> No match"); + return false; + } + + return true; + } +} +bool exception_matches_list(const std::type_info *throw_type, const struct sLSDA_Header &header, int list_index) +{ + _SysDebug("TODO: exception_matches_list %i", list_index); + abort(); + (void)throw_type; + (void)header; + return true; +} + +template T read(const void *&ptr) +{ + T rv = *reinterpret_cast(ptr); + ptr = (const char*)ptr + sizeof(T); + return rv; +} + +static void read_lsda_header(const void *&ptr, _Unwind_Context *context, struct sLSDA_Header *header) +{ + header->Base = ptr; + + uint8_t start_encoding = read(ptr); + if( start_encoding == DW_EH_PE_omit ) + header->LPStart = _Unwind_GetRegionStart(context); + else + header->LPStart = _read_encoded(ptr, context, start_encoding); + + header->TTEncoding = read(ptr); + if( header->TTEncoding == DW_EH_PE_omit ) + header->TTBase = 0; + else { + ptrdiff_t tt_ofs = _read_leb128u(ptr); + header->TTBase = (uintptr_t)ptr + tt_ofs; + } + header->TypePtrBase = _get_base(header->TTEncoding, context); + + header->CallSiteEncoding = read(ptr); + uint64_t call_site_size = _read_leb128u(ptr); + header->CallSiteTable = ptr; + header->ActionTable = (const void*)( (uintptr_t)ptr + call_site_size ); + + #if 0 + ::_SysDebug("LSDA header:"); + ::_SysDebug("->LPStart = 0x%lx", header->LPStart); + ::_SysDebug("->TTEncoding = 0x%02x", header->TTEncoding); + ::_SysDebug("->TTBase = 0x%lx", header->TTBase); + ::_SysDebug("->CallSiteEncoding = 0x%02x", header->CallSiteEncoding); + ::_SysDebug("->CallSiteTable = %p", header->CallSiteTable); + ::_SysDebug("->ActionTable = %p", header->ActionTable); + #endif +} + +static size_t _get_encoded_size(int encoding) +{ + switch(encoding & DW_EH_PE_fmtmask) + { + case DW_EH_PE_absptr: // absolute + return sizeof(void*); + case DW_EH_PE_udata4: + return 4; + default: + _SysDebug("_get_encoded_size: Unknown encoding 0x%02x", encoding); + return 0; + } +} + +/* + * \brief Read a DWARF encoded pointer from the stream + */ +static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding, uint64_t base) +{ + (void)context; + if( encoding == DW_EH_PE_aligned ) { + void **aligned = (void**)( ((uintptr_t)ptr + sizeof(void*) - 1) & -(sizeof(void*)-1) ); + ptr = (void*)( (uintptr_t)aligned + sizeof(void*) ); + return (uintptr_t)*aligned; + } + uint64_t rv; + uintptr_t orig_ptr = (uintptr_t)ptr; + switch(encoding & DW_EH_PE_fmtmask) + { + case DW_EH_PE_absptr: // absolute + rv = (uintptr_t)read(ptr); + break; + case DW_EH_PE_uleb128: + rv = _read_leb128u(ptr); + break; + case DW_EH_PE_sleb128: + rv = _read_leb128s(ptr); + break; + case DW_EH_PE_udata2: + rv = read(ptr); + break; + case DW_EH_PE_udata4: + rv = read(ptr); + break; + case DW_EH_PE_udata8: + rv = read(ptr); + break; + default: + ::_SysDebug("_read_encoded: Unknown encoding format 0x%x", encoding & DW_EH_PE_fmtmask); + ::abort(); + } + if( rv != 0 ) + { + if( (encoding & DW_EH_PE_relmask) == DW_EH_PE_pcrel ) { + rv += orig_ptr; + } + else { + rv += base; + } + + if( encoding & DW_EH_PE_indirect ) { + rv = (uintptr_t)*(void**)(uintptr_t)rv; + } + else { + // nothing + } + } + return rv; +} +static uint64_t _get_base(uint8_t encoding, _Unwind_Context *context) +{ + if( encoding == 0xFF ) + return 0; + switch(encoding & DW_EH_PE_relmask) + { + case DW_EH_PE_absptr: + case DW_EH_PE_pcrel: + case DW_EH_PE_aligned: + return 0; + //case DW_EH_PE_textrel: + //return _Unwind_GetTextRelBase(context); + //case DW_EH_PE_datarel: + //return _Unwind_GetDataRelBase(context); + case DW_EH_PE_funcrel: + return _Unwind_GetRegionStart(context); + default: + ::_SysDebug("_get_base: Unknown encoding relativity 0x%x", (encoding & DW_EH_PE_relmask)); + ::abort(); + for(;;); + } +} +static uint64_t _read_encoded(const void *&ptr, _Unwind_Context *context, int encoding) +{ + return _read_encoded(ptr, context, encoding, _get_base(encoding, context)); +} + +static uint64_t _read_leb128_raw(const void *&ptr, int *sizep) +{ + int size = 0; + uint64_t val = 0; + const uint8_t *ptr8 = static_cast(ptr); + do + { + val |= (*ptr8 & 0x7F) << (size*7); + size ++; + } while( *ptr8++ & 0x80 ); + + ptr = ptr8; + *sizep = size; + + return val; +} + +static uint64_t _read_leb128u(const void *&ptr) +{ + int unused_size; + return _read_leb128_raw(ptr, &unused_size); +} +static int64_t _read_leb128s(const void *&ptr) +{ + int size; + uint64_t val = _read_leb128_raw(ptr, &size); + if( size*7 <= 64 && (val & (1 << (size*7-1))) ) + { + val |= 0xFFFFFFFFFFFFFFFF << (size*7); + } + return val; +} +