--- /dev/null
+/*
+ * Acess2 C++ Support Library
+ * - By John Hodge (thePowersGang)
+ *
+ * exception_handling_cxxabi.h
+ * - C++ Exception Handling definitions from the Itanium C++ ABI
+ */
+#ifndef _EXCEPTION_HANLDING_CXXABI_H_
+#define _EXCEPTION_HANLDING_CXXABI_H_
+
+typedef void *(unexpected_handler)(void);
+typedef void *(terminate_handler)(void);
+
+struct _Unwind_Context;
+
+typedef enum {
+ _URC_NO_REASON = 0,
+ _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
+ _URC_FATAL_PHASE2_ERROR = 2,
+ _URC_FATAL_PHASE1_ERROR = 3,
+ _URC_NORMAL_STOP = 4,
+ _URC_END_OF_STACK = 5,
+ _URC_HANDLER_FOUND = 6,
+ _URC_INSTALL_CONTEXT = 7,
+ _URC_CONTINUE_UNWIND = 8
+} _Unwind_Reason_Code;
+
+typedef int _Unwind_Action;
+static const _Unwind_Action _UA_SEARCH_PHASE = 1;
+static const _Unwind_Action _UA_CLEANUP_PHASE = 2;
+static const _Unwind_Action _UA_HANDLER_FRAME = 4;
+static const _Unwind_Action _UA_FORCE_UNWIND = 8;
+
+typedef void (*_Unwind_Exception_Cleanup_Fn)(_Unwind_Reason_Code reason, struct _Unwind_Exception *exc);
+
+struct _Unwind_Exception
+{
+ uint64_t exception_class;
+ _Unwind_Exception_Cleanup_Fn exception_cleanup;
+ uint64_t private_1;
+ uint64_t private_2;
+};
+
+struct __cxa_exception
+{
+ std::type_info* exceptionType;
+ void (*exceptionDestructor)(void *);
+ unexpected_handler* unexpectedHandler;
+ terminate_handler* terminateHandler;
+ __cxa_exception* nextException;
+
+ int handlerCount;
+
+ int handlerSwitchValue;
+ const char* actionRecord;
+ const char* languageSpecificData;
+ void* catchTemp;
+ void* adjustedPtr;
+
+ _Unwind_Exception unwindHeader;
+};
+
+#define EXCEPTION_CLASS_ACESS "Ac20C++\0"
+
+typedef _Unwind_Reason_Code (*_Unwind_Stop_Fn)(int version, _Unwind_Action actions, uint64_t exception_class, struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context, void *stop_parameter);
+
+// Exports
+extern "C" void __cxa_call_unexpected(void *);
+extern "C" void *__cxa_allocate_exception(size_t thrown_size);
+extern "C" void __cxa_free_exception(void *thrown_exception);
+extern "C" void __cxa_throw(void *thrown_exception, std::type_info *tinfo, void (*dest)(void*));
+extern "C" void *__cxa_begin_catch(_Unwind_Exception *exceptionObject);
+extern "C" void __cxa_end_catch();
+
+// Imports
+extern "C" _Unwind_Reason_Code _Unwind_RaiseException(struct _Unwind_Exception *exception_object);
+extern "C" _Unwind_Reason_Code _Unwind_ForcedUnwind(struct _Unwind_Exception *exception_object, _Unwind_Stop_Fn stop, void *stop_parameter);
+extern "C" void _Unwind_DeleteException(struct _Unwind_Exception *exception_object);
+extern "C" uint64_t _Unwind_GetGR(struct _Unwind_Context *context, int index);
+extern "C" void _Unwind_SetGR(struct _Unwind_Context *context, int index, uint64_t new_value);
+extern "C" uint64_t _Unwind_GetIP(struct _Unwind_Context *context);
+extern "C" void _Unwind_SetIP(struct _Unwind_Context *context, uint64_t new_value);
+extern "C" uint64_t _Unwind_GetLanguageSpecificData(struct _Unwind_Context *context);
+extern "C" uint64_t _Unwind_GetRegionStart(struct _Unwind_Context *context);
+
+#endif
+
+
--- /dev/null
+/*
+ * Acess2 C++ Support Library
+ * - By John Hodge (thePowersGang)
+ *
+ * gxx_personality.cc
+ * - Implementation of GNU C++ Exception handling "Personality"
+ */
+#include <typeinfo>
+#include <cstdint> // [u]intN_t
+#include <cstdlib> // abort
+#include <cstring> // memcmp
+#include "exception_handling_acxx.h"
+#include "exception_handling_cxxabi.h"
+#include <acess/sys.h>
+#include <cassert>
+#include <cxxabi.h> // __dynamic_cast
+
+// === 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 <typename T> 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<const __cxa_exception*>(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
+ _SysDebug("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
+ _SysDebug("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;
+ }
+
+ _SysDebug("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);
+ _SysDebug("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!
+ _SysDebug("__gxx_personality_v0: No entry for IP 0x%x", ip);
+ return 0;
+ }
+
+ // Found it
+ if( cs_ldgpad == 0 ) {
+ _SysDebug("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 ) {
+ _SysDebug("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);
+ _SysDebug("filter=%lli,disp=%lli", filter, disp);
+ 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);
+ _SysDebug("typeinfo_ptr = %p", type_ptr);
+ 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);
+
+ if( !catch_type )
+ {
+ _SysDebug("catch(...)");
+ return true;
+ }
+ else if( !throw_type )
+ {
+ _SysDebug("threw UNK");
+ return false;
+ }
+ else
+ {
+ _SysDebug("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);
+ (void)throw_type;
+ (void)header;
+ return true;
+}
+
+template <typename T> T read(const void *&ptr)
+{
+ T rv = *reinterpret_cast<const T*>(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<uint8_t>(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<uint8_t>(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<uint8_t>(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*);
+ 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<void*>(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<uint16_t>(ptr);
+ break;
+ case DW_EH_PE_udata4:
+ rv = read<uint32_t>(ptr);
+ break;
+ case DW_EH_PE_udata8:
+ rv = read<uint64_t>(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();
+ }
+}
+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<const uint8_t*>(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;
+}
+