diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp --- a/libcxxabi/src/cxa_personality.cpp +++ b/libcxxabi/src/cxa_personality.cpp @@ -1243,6 +1243,563 @@ std::__terminate(t_handler); } +#if defined(_AIX) + +#include + +// Macros for debugging state table parsing. +#ifdef NDEBUG +#define _LIBCXXABI_TRACE_STATETAB(_args...) +#define _LIBCXXABI_TRACE_STATETAB0(_args...) +#define _LIBCXXABI_TRACING_STATETAB (0) +#else +bool StateTabDbg() { + static bool checked = false; + static bool log = false; + if (!checked) { + log = (getenv("LIBCXXABI_PRINT_STATTAB") != NULL); + checked = true; + } + return log; +} + +#define _LIBCXXABI_TRACE_STATETAB(_args...) \ + do { \ + if (StateTabDbg()) \ + fprintf(stderr, "libcxxabi: " _args); \ + } while (0) +#define _LIBCXXABI_TRACE_STATETAB0(_args...) \ + do { \ + if (StateTabDbg()) \ + fprintf(stderr, _args); \ + } while (0) +#define _LIBCXXABI_TRACING_STATETAB StateTabDbg() +#endif + +typedef void (*destruct_f)(void*); + +// The finite state machine to be walked. +struct FSM_Entry { + long offset; + union { + void (*destructor)(void*, size_t); + void* catch_block; + }; + long count; + size_t elem_size; + unsigned short flags; + unsigned short next_state; +}; + +struct FSM { + unsigned int magic; + int number_of_states; + FSM_Entry table[1]; // Actually table[number_of_states] +}; + +// The state variable on the stack +struct StateVariable { + int state; + struct FSM* table; + long this_value; + int ignore_vbase_ptrs; +}; + +#define FSM_MAGIC 0xbeefdead +#define FSM_MAGIC2 0xbeeedead +#define FSM_MAGIC3 0x1cedbeef + +#define BEGIN_CATCH_COUNT -1 +#define END_CATCH_COUNT -2 +#define DELETE_OBJECT_COUNT -3 +#define INLINE_DESTRUCTOR_COUNT -4 +#define TERMINATE_COUNT -5 + +#define STATE_CHANGE_OLD \ + 0x400 // State table entry is an indirect state \ + // change, dereference the address in \ + // offset as int for the target state. \ + // This is deplicated. This indicate \ + // the address is direct. (static local) +#define STATE_CHANGE \ + 0x800 // State table entry is an indirect state \ + // change, dereference the address in \ + // offset as int for the target state. \ + // The temporary is an automatic. + +#define THIS_FLAG 0x01 +#define VBASE_FLAG 0x02 +#define GLOBAL_OBJ 0x04 + +#define INDIRECT \ + 0x100 // Object was thrown from a function \ + // where the return value optimization \ + // was used. +#define DTR_ARGUMENT 0x2 // Free virtual bases, don't delete object + +static void __Invoke__Destructor(FSM_Entry* fsmEntry, void* addr) { + _LIBCXXABI_TRACE_STATETAB("Deallocate object=%p, fsmEntry=%p\n", addr, + (void*)fsmEntry); + try { + if (fsmEntry->count == 1) { + _LIBCXXABI_TRACE_STATETAB("calling scaler destructor\n"); + (*fsmEntry->destructor)(addr, DTR_ARGUMENT); + _LIBCXXABI_TRACE_STATETAB("returned from scaler destructor\n"); + } else { + _LIBCXXABI_TRACE_STATETAB("calling vector destructor\n"); + __cxa_vec_cleanup(addr, (size_t)fsmEntry->count, fsmEntry->elem_size, + (destruct_f)fsmEntry->destructor); + _LIBCXXABI_TRACE_STATETAB("returned from vector destructor\n"); + } + } catch (...) { + _LIBCXXABI_TRACE_STATETAB("Uncaught exception in destructor, " + "terminating\n"); + std::terminate(); + } +} + +static void __Invoke__Delete(FSM_Entry* fsmEntry, void* addr) { + char* objectAddress = *(char**)addr; + + _LIBCXXABI_TRACE_STATETAB("Delete object=%p, fsmEntry=%p\n", + (void*)objectAddress, (void*)fsmEntry); + try { + _LIBCXXABI_TRACE_STATETAB("..calling delete()\n"); + // 'destructor' holds a function pointer to delete(). + (*fsmEntry->destructor)(objectAddress, fsmEntry->elem_size); + _LIBCXXABI_TRACE_STATETAB("..returned from delete()\n"); + } catch (...) { + _LIBCXXABI_TRACE_STATETAB("Uncaught exception in delete(), " + "terminating\n"); + std::terminate(); + } +} + +// __xlc_catch_matchv2 +// Check whether the thrown object matches the catch handler's exception +// declaration. If there is a match, the function returns true with adjusted +// address of the thrown object. Otherwise, return false. +extern "C" bool __xlc_catch_matchv2(_Unwind_Exception* exceptionObject, + std::type_info* catch_ti, void*& obj) { + _LIBCXXABI_TRACE_STATETAB("Entering %s, exceptionObject=%p\n", __func__, + (void*)exceptionObject); + + if (!__isOurExceptionClass(exceptionObject)) { + // If it is not a c++ exception, return 0 to indicate no match + _LIBCXXABI_TRACE_STATETAB("No match, not a C++ exception\n"); + return false; + } + + __cxa_exception* exceptionHeader = 0; + + if (__getExceptionClass(exceptionObject) == kOurDependentExceptionClass) { + // Walk to the __cxa_dependent_exception primary exception for the + // exception object and its type_info. + __cxa_dependent_exception* dependentExceptionHeader = + (__cxa_dependent_exception*)(exceptionObject + 1) - 1; + exceptionHeader = static_cast<__cxa_exception*>( + dependentExceptionHeader->primaryException) - + 1; + _LIBCXXABI_TRACE_STATETAB("exceptionObject 0x%p is a dependent, " + "primary 0x%p\n", + (void*)exceptionObject, + (void*)&exceptionHeader->unwindHeader); + exceptionObject = &exceptionHeader->unwindHeader; + } else { + _LIBCXXABI_TRACE_STATETAB("exceptionObject %p is NOT a dependent\n", + (void*)exceptionObject); + exceptionHeader = ((__cxa_exception*)(exceptionObject + 1) - 1); + } + + void* thrownObject = (void*)(exceptionObject + 1); + std::type_info* throw_ti = exceptionHeader->exceptionType; + + // Get the type info for the thrown type and this catch clause and + // see if the catch caluse can catch that type. + + __cxxabiv1::__shim_type_info* catchType = + static_cast<__cxxabiv1::__shim_type_info*>(catch_ti); + __cxxabiv1::__shim_type_info* throwType = + static_cast<__cxxabiv1::__shim_type_info*>(throw_ti); + _LIBCXXABI_TRACE_STATETAB("UnwindException=%p thrownObject=%p " + "t-ti=%p(%s) c-ti=%p(%s)\n", + (void*)exceptionObject, thrownObject, + (void*)throwType, throwType->name(), + (void*)catchType, catchType->name()); + if (catchType->can_catch(throwType, thrownObject)) { + exceptionHeader->adjustedPtr = thrownObject; + obj = thrownObject; + _LIBCXXABI_TRACE_STATETAB("Match found for thrownObject=%p\n", + thrownObject); + return true; + } + _LIBCXXABI_TRACE_STATETAB("No match\n"); + return false; +} + +const uint32_t gRegisterNum = 14; // The GPR to pass the exception object + // address for xlclang++ compiled code. + +// Calculate the object address from the FSM entry +static void* compute_obj_addr(FSM_Entry* fsmEntry, StateVariable* const state) { + void* addr; + if (fsmEntry->flags & GLOBAL_OBJ) { + addr = (void*)fsmEntry->offset; + _LIBCXXABI_TRACE_STATETAB("Address calculation (global obj) " + "addr=currEntry->offset=%p\n", + addr); + } else if (fsmEntry->flags & THIS_FLAG) { + addr = (void*)(fsmEntry->offset + state->this_value); + _LIBCXXABI_TRACE_STATETAB("Address calculation (this obj) " + "currEntry->offset=%ld : " + "state->this_value=%ld addr=" + "(currEntry-->offset+state->this_value)=%p\n", + fsmEntry->offset, state->this_value, addr); + } else if (fsmEntry->flags & INDIRECT) { + addr = (void*)*(char**)(fsmEntry->offset); + _LIBCXXABI_TRACE_STATETAB("Address calculation (indirect obj) " + "addr=%p, fsmEntry->offset=%ld \n", + addr, fsmEntry->offset); + } else { + addr = (void*)(fsmEntry->offset); + _LIBCXXABI_TRACE_STATETAB("Address calculation. (local obj) " + "addr=currEntry->offset=%p\n", + addr); + } + return addr; +} + +static void scan_state_tab(scan_results& results, _Unwind_Action actions, + bool native_exception, + _Unwind_Exception* unwind_exception, + _Unwind_Context* context) { + // Initialize results to found nothing but an error + results.ttypeIndex = 0; + results.actionRecord = 0; + results.languageSpecificData = 0; + results.landingPad = 0; + results.adjustedPtr = 0; + results.reason = _URC_FATAL_PHASE1_ERROR; + + // Check for consistent actions. + if (actions & _UA_SEARCH_PHASE) { + // Do Phase 1 + if (actions & (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME | _UA_FORCE_UNWIND)) { + // None of these flags should be set during Phase 1 + // Client error + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } + } else if (actions & _UA_CLEANUP_PHASE) { + if ((actions & _UA_HANDLER_FRAME) && (actions & _UA_FORCE_UNWIND)) { + // _UA_HANDLER_FRAME should only be set if phase 1 found a handler. + // If _UA_FORCE_UNWIND is set, phase 1 shouldn't have happened. + // Client error + results.reason = _URC_FATAL_PHASE2_ERROR; + return; + } + } else { + // Neither _UA_SEARCH_PHASE nor _UA_CLEANUP_PHASE is set. + // Client error + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } + + if (_LIBCXXABI_TRACING_STATETAB) { + _LIBCXXABI_TRACE_STATETAB0("\n"); + _LIBCXXABI_TRACE_STATETAB("%s: actions=%d (", __func__, actions); + + if (_UA_SEARCH_PHASE & actions) + _LIBCXXABI_TRACE_STATETAB0("_UA_SEARCH_PHASE "); + if (_UA_CLEANUP_PHASE & actions) + _LIBCXXABI_TRACE_STATETAB0("_UA_CLEANUP_PHASE "); + if (_UA_HANDLER_FRAME & actions) + _LIBCXXABI_TRACE_STATETAB0("_UA_HANDLER_FRAME "); + if (_UA_FORCE_UNWIND & actions) + _LIBCXXABI_TRACE_STATETAB0("_UA_FORCE_UNWIND "); + _LIBCXXABI_TRACE_STATETAB0(")\n"); + _LIBCXXABI_TRACE_STATETAB(" unwind_exception=%p context=%p\n", + (void*)unwind_exception, (void*)context); + } + + // Start scan by getting state table address + StateVariable* const state = + (StateVariable* const)_Unwind_GetLanguageSpecificData(context); + if (state->state <= 0) { + // The state is not correct - give up on this routine + _LIBCXXABI_TRACE_STATETAB("state=%d and is <= 0), continue unwinding\n", + state->state); + results.reason = _URC_CONTINUE_UNWIND; + return; + } + // Parse the state table + FSM* const fsm = state->table; + + _LIBCXXABI_TRACE_STATETAB("state->state=%d, state->ignore_vbase_ptrs=%d\n", + state->state, state->ignore_vbase_ptrs); + + // The magic number is FSM_MAGIC3 for xlclang++. + _LIBCXXABI_TRACE_STATETAB("fsm->magic=%#x, fsm->number_of_states=%d\n", + fsm->magic, fsm->number_of_states); + + if (fsm->magic == FSM_MAGIC || fsm->magic == FSM_MAGIC2 || + fsm->magic == FSM_MAGIC3) { + // Debug display of the FSM table. + if (_LIBCXXABI_TRACING_STATETAB) { + // Print out the FSM table. + _LIBCXXABI_TRACE_STATETAB("FSM table:\n"); + _LIBCXXABI_TRACE_STATETAB("%12s %10s %8s %10s %7s %7s %7s %7s\n", + "Entry Addr", "state", "Offset", "DTR addr", + "count", "el_size", "flags", "next"); + for (int i = 0; i < fsm->number_of_states; i++) { + FSM_Entry& f = fsm->table[i]; + _LIBCXXABI_TRACE_STATETAB("%12p (%8d) %8ld %10p %7ld " + "%7ld %#7x %7d\n", + (void*)&f, i + 1, f.offset, + (void*)f.destructor, f.count, f.elem_size, + f.flags, f.next_state); + } + } + } else { + // Something is wrong the the state table we found. + if (_UA_SEARCH_PHASE & actions) { + _LIBCXXABI_TRACE_STATETAB("Invalid FSM table, " + "return _URC_FATAL_PHASE1_ERROR\n"); + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } else if (_UA_CLEANUP_PHASE & actions) { + _LIBCXXABI_TRACE_STATETAB("Invalid FSM table, " + "return _URC_FATAL_PHASE2_ERROR\n"); + results.reason = _URC_FATAL_PHASE2_ERROR; + return; + } else { + // We should never get here. + _LIBCXXABI_TRACE_STATETAB("Invalid FSM table + RT Internal error," + " return _URC_FATAL_PHASE2_ERROR\n"); + results.reason = _URC_FATAL_PHASE2_ERROR; + return; + } + } + + if (_UA_SEARCH_PHASE & actions) { + // Start walking the table. Use a local copy of state->state so when + // we return from search phase we don't change the state number. + int currState = state->state; + + while (currState > 0) { + FSM_Entry* currEntry = &fsm->table[currState - 1]; + _LIBCXXABI_TRACE_STATETAB("Processing state=%d, flags=0x%hx\n", currState, + currEntry->flags); + + void* addr = compute_obj_addr(currEntry, state); + + if (currEntry->count == BEGIN_CATCH_COUNT) { + // Found a catch handler. + _LIBCXXABI_TRACE_STATETAB("Found a catch handler, " + "return _URC_HANDLER_FOUND\n"); + results.reason = _URC_HANDLER_FOUND; + return; + } else if (currEntry->count == TERMINATE_COUNT) { + _LIBCXXABI_TRACE_STATETAB("Found the terminate state, " + "return _URC_HANDLER_FOUND\n"); + results.reason = _URC_HANDLER_FOUND; + return; + } else if (currEntry->flags & STATE_CHANGE_OLD) { + // Deplicated conditional expression. + currState = *((int*)(currEntry->offset)); + _LIBCXXABI_TRACE_STATETAB("Flag: STATE_CHANGE_OLD, dereference " + "currEntry->offset(%ld), set state=%d\n", + currEntry->offset, currState); + continue; // We are done this iteration of the loop, since + // we changed a state. + } else if (currEntry->flags & STATE_CHANGE) { + state->state = *((int*)(addr)); + _LIBCXXABI_TRACE_STATETAB("Flag: STATE_CHANGE, dereference " + "addr(%p), set state=%d\n", + addr, state->state); + continue; // We are done this iteration of the loop, since we + // changed the state. + } + + // Go to the next state. + currState = currEntry->next_state; + } + _LIBCXXABI_TRACE_STATETAB("No catch handler found, " + "return _URC_CONTINUE_UNWIND\n"); + results.reason = _URC_CONTINUE_UNWIND; + return; + } else if (_UA_CLEANUP_PHASE & actions) { + // Start walking the table. + while (state->state > 0) { + FSM_Entry* currEntry = &fsm->table[state->state - 1]; + + if (currEntry->count == TERMINATE_COUNT) { + _LIBCXXABI_TRACE_STATETAB("Reached terminate state. " + "Call terminate.\n"); + std::terminate(); + } + + void* addr = compute_obj_addr(currEntry, state); + + // Perform action according to the currEntry->count, + // except when flag is STATE_CHANGE or STATE_CHANGE_OLD. + _LIBCXXABI_TRACE_STATETAB("Processing state=%d, flags=0x%hx\n", + state->state, currEntry->flags); + if (currEntry->flags & STATE_CHANGE_OLD) { + state->state = *((int*)(currEntry->offset)); + _LIBCXXABI_TRACE_STATETAB("Flag: STATE_CHANGE_OLD, dereference " + "currEntry->offset(%ld), set state=%d\n", + currEntry->offset, state->state); + continue; // We are done with this iteration of the loop, since + // we changed a state. + } else if (currEntry->flags & STATE_CHANGE) { + state->state = *((int*)(addr)); + _LIBCXXABI_TRACE_STATETAB("Flag: STATE_CHANGE, dereference " + "addr(%p), set state=%d\n", + addr, state->state); + continue; // We are done with this iteration of the loop, since + // we changed a state. + } else if (currEntry->count > 0) { + if (currEntry->flags & VBASE_FLAG && state->ignore_vbase_ptrs) { + _LIBCXXABI_TRACE_STATETAB("Ignoring virtual base dtor.\n"); + } else { + _LIBCXXABI_TRACE_STATETAB("Invoke dtor for object=%p\n", addr); + __Invoke__Destructor(currEntry, addr); + } + } else if (currEntry->count == DELETE_OBJECT_COUNT) { + _LIBCXXABI_TRACE_STATETAB("Delete object at %p\n", addr); + __Invoke__Delete(currEntry, addr); + } else if (currEntry->count == BEGIN_CATCH_COUNT) { + // Branch to the landing pad to see if the exception can be + // caught. + _LIBCXXABI_TRACE_STATETAB("BEGIN_CATCH_COUNT: handler " + "%p/%p, return _URC_HANDLER_FOUND\n", + currEntry->catch_block, + *(void**)(currEntry->catch_block)); + _LIBCXXABI_TRACE_STATETAB("Changed to state=%d\n", state->state); + state->state = currEntry->next_state; + results.landingPad = (uintptr_t) * (void**)(currEntry->catch_block); + results.reason = _URC_HANDLER_FOUND; + return; + } else if (currEntry->count == END_CATCH_COUNT) { + // End current catch block and branch to the landing pad. + _LIBCXXABI_TRACE_STATETAB("END_CATCH_COUNT: handler " + "%p/%p, return _URC_HANDLER_FOUND\n", + currEntry->catch_block, + *(void**)(currEntry->catch_block)); + _LIBCXXABI_TRACE_STATETAB("Changed to state=%d\n", state->state); + state->state = currEntry->next_state; + results.landingPad = (uintptr_t) * (void**)(currEntry->catch_block); + results.reason = _URC_HANDLER_FOUND; + return; + } else if (currEntry->count == INLINE_DESTRUCTOR_COUNT) { + // Branch to user code to perform the action and eventually + // the inline unwinding will call _Unwind_resume. + _LIBCXXABI_TRACE_STATETAB("INLINE_DESTRUCTOR_COUNT: handler " + "%p/%p, return _URC_HANDLER_FOUND\n", + currEntry->catch_block, + *(void**)(currEntry->catch_block)); + _LIBCXXABI_TRACE_STATETAB("Changed to state=%d\n", state->state); + state->state = currEntry->next_state; + results.landingPad = (uintptr_t) * (void**)(currEntry->catch_block); + results.reason = _URC_HANDLER_FOUND; + return; + } else { + _LIBCXXABI_TRACE_STATETAB("Unknown entry in FSM (count=%ld), " + "ignored\n", + currEntry->count); + } // End of action switching + + // Go to next state. + state->state = currEntry->next_state; + } + _LIBCXXABI_TRACE_STATETAB("No catch handler, " + "return _URC_CONTINUE_UNWIND\n"); + results.reason = _URC_CONTINUE_UNWIND; + return; + } + _LIBCXXABI_TRACE_STATETAB("No state table entry for this exception, " + "call_terminate()\n"); + // It is possible that no state table entry specify how to handle + // this exception. By spec, terminate it immediately. + call_terminate(native_exception, unwind_exception); +} + +// Personality routine for EH using state table. +_Unwind_Reason_Code __xlcxx_personality_v0(int version, _Unwind_Action actions, + uint64_t exceptionClass, + _Unwind_Exception* unwind_exception, + _Unwind_Context* context) { + if (version != 1 || unwind_exception == 0 || context == 0) + return _URC_FATAL_PHASE1_ERROR; + + bool native_exception = (exceptionClass & get_vendor_and_language) == + (kOurExceptionClass & get_vendor_and_language); + scan_results results; + scan_state_tab(results, actions, native_exception, unwind_exception, context); + if (actions & _UA_SEARCH_PHASE) { + // Phase 1 search: All we're looking for in phase 1 is a handler that + // halts unwinding + return results.reason; + } + if (actions & _UA_CLEANUP_PHASE) { + // Phase 2 cleanup: + if (results.reason == _URC_HANDLER_FOUND) { + // Jump to the handler. + _Unwind_SetGR(context, gRegisterNum, + reinterpret_cast(unwind_exception)); + _Unwind_SetIP(context, results.landingPad); + return _URC_INSTALL_CONTEXT; + } + // Did not find a handler. Return the results of the scan. Normally + // _URC_CONTINUE_UNWIND, but could have been _URC_FATAL_PHASE2_ERROR. + return results.reason; + } + // We were called improperly: neither a phase 1 or phase 2 search. + return _URC_FATAL_PHASE1_ERROR; +} + +// Personality routine for EH using the range table. Make it an alias of +// __gxx_personality_v0(). +_LIBCXXABI_FUNC_VIS _Unwind_Reason_Code __xlcxx_personality_v1( + int version, _Unwind_Action actions, uint64_t exceptionClass, + _Unwind_Exception* unwind_exception, _Unwind_Context* context) + __attribute__((alias("__gxx_personality_v0"))); + +// __xlc_throw_badexception +// This function is for xlclang++. It allocates and throws a badexception. +// During unwinding for this badexception, the previous exception which is +// not matching the throw spec. will be cleaned up. Thus having the same +// effect as replace the top most exception (which is bad) with a badexception. +extern "C" void __xlc_throw_badexception() { + _LIBCXXABI_TRACE_STATETAB("Entering function: %s\n\n", __func__); + void* newexception = __cxa_allocate_exception(sizeof(std::bad_exception)); + __cxa_throw(newexception, (std::type_info*)&typeid(std::bad_exception), 0); +} + +// __xlc_exception_handle +// This function is for xlclang++. It returns the address of the exception +// object set in gpr14 by the personality routine for xlclang++ compiled code. +uintptr_t __xlc_exception_handle() { + //register unsigned long exceptionObject asm("r14"); + uintptr_t exceptionObject; + + asm("mr %0, 14" : "=r"(exceptionObject)); + return exceptionObject; +} + +// This function is for xlclang++. +void __UnsupportedConditionalExpressionDestruction(void* addr, long flag) { + // We want to die instead of randomlly picking b or c to destory, which + // will be confusing for the user. + abort(); +} + +// This function is for xlclang++. It aborts when a deleted virtual +// function is called. +void __Deleted_Virtual() { abort(); } +#endif // defined(_AIX) + } // extern "C" } // __cxxabiv1