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 @@ -32,6 +32,11 @@ _Unwind_Personality_Fn); #endif +#if defined(_AIX) +#include +#include +#endif + /* Exception Header Layout: @@ -1286,7 +1291,600 @@ int version, _Unwind_Action actions, uint64_t exceptionClass, _Unwind_Exception* unwind_exception, _Unwind_Context* context) __attribute__((__alias__("__gxx_personality_v0"))); -#endif + +// Code for the personality of the state table. + +// 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 // NDEBUG + +typedef void (*destruct_f)(void*); + +// The finite state machine to be walked. +struct FSMEntry { + intptr_t offset; + union { + void (*destructor)(void*, size_t); + void* catchBlock; + }; + intptr_t count; + size_t elemSize; + uint16_t flags; + uint16_t nextState; +}; + +struct FSM { + uint32_t magic; + int32_t numberOfStates; + FSMEntry table[]; // Actually table[numberOfStates] +}; + +// The state variable on the stack +struct StateVariable { + int32_t state; + struct FSM* table; + intptr_t thisValue; + int32_t ignoreVBasePtrs; +}; + +// State table magic number +enum FSMMagic : uint32_t { + number0 = 0xbeefdead, // xlC compiler + number2 = 0xbeeedead, + number3 = 0x1cedbeef // xlclang++ compiler +}; + +// Definition of state table entry count +enum FSMEntryCount : intptr_t { + beginCatch = -1, + endCatch = -2, + deleteObject = -3, + inlineDestructor = -4, + terminate = -5 +}; + +// Definition of state table entry flag +enum FSMEntryFlag : int16_t { + indirect = 0x100, // Object was thrown from a function where + // the return value optimization was used + stateChangeOld = 0x400, // State table entry is an indirect state + // change, dereference the address in offset + // as int for the target state. + // This is deplicated. This indicates + // the address is direct. (static local) + stateChange = 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 + thisFlag = 0x01, + vBaseFlag = 0x02, + globalObj = 04 +}; + +const uint32_t gRegisterNum = 14; // The GPR to pass the exception object + // address for xlclang++ compiled code. + +#define DTR_ARGUMENT 0x2 // Free virtual bases, don't delete object + +static void __Invoke__Destructor(FSMEntry* fsmEntry, void* addr) { + _LIBCXXABI_TRACE_STATETAB("Deallocate object=%p, fsmEntry=%p\n", addr, + (void*)fsmEntry); + try { + if (fsmEntry->count == 1) { + _LIBCXXABI_TRACE_STATETAB("calling scalar destructor\n"); + (*fsmEntry->destructor)(addr, DTR_ARGUMENT); + _LIBCXXABI_TRACE_STATETAB("returned from scalar destructor\n"); + } else { + _LIBCXXABI_TRACE_STATETAB("calling vector destructor\n"); + __cxa_vec_cleanup(addr, (size_t)fsmEntry->count, fsmEntry->elemSize, + (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(FSMEntry* 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->elemSize); + _LIBCXXABI_TRACE_STATETAB("..returned from delete()\n"); + } catch (...) { + _LIBCXXABI_TRACE_STATETAB("Uncaught exception in delete(), " + "terminating\n"); + std::terminate(); + } +} + +// Calculate the object address from the FSM entry +static void* compute_obj_addr(FSMEntry* fsmEntry, StateVariable* const state) { + void* addr; + if (fsmEntry->flags & FSMEntryFlag::globalObj) { + addr = (void*)fsmEntry->offset; + _LIBCXXABI_TRACE_STATETAB("Address calculation (global obj) " + "addr=currEntry->offset=%p\n", + addr); + } else if (fsmEntry->flags & FSMEntryFlag::thisFlag) { + addr = (void*)(fsmEntry->offset + state->thisValue); + _LIBCXXABI_TRACE_STATETAB("Address calculation (this obj) " + "currEntry->offset=%ld : " + "state->thisValue=%ld addr=" + "(currEntry->offset+state->thisValue)=%p\n", + fsmEntry->offset, state->thisValue, addr); + } else if (fsmEntry->flags & FSMEntryFlag::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->ignoreVBasePtrs=%d\n", + state->state, state->ignoreVBasePtrs); + + _LIBCXXABI_TRACE_STATETAB("fsm->magic=%#x, fsm->numberOfStates=%d\n", + fsm->magic, fsm->numberOfStates); + + if (fsm->magic == FSMMagic::number0 || fsm->magic == FSMMagic::number2 || + fsm->magic == FSMMagic::number3) { + // 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->numberOfStates; i++) { + FSMEntry& 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.elemSize, + f.flags, f.nextState); + } + } + } 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) { + FSMEntry* 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 == FSMEntryCount::beginCatch) { + // Found a catch handler. + if (fsm->magic == FSMMagic::number0) { + _LIBCXXABI_TRACE_STATETAB("Found a xlC catch handler, " + "return _URC_FATAL_PHASE1_ERROR\n"); + // xlC catch handlers cannot be entered because they use a + // proprietary EH runtime that is not interoperable. + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } + _LIBCXXABI_TRACE_STATETAB("Found a catch handler, " + "return _URC_HANDLER_FOUND\n"); + results.reason = _URC_HANDLER_FOUND; + return; + } else if (currEntry->count == FSMEntryCount::terminate) { + _LIBCXXABI_TRACE_STATETAB("Found the terminate state, " + "return _URC_HANDLER_FOUND\n"); + results.reason = _URC_HANDLER_FOUND; + return; + } else if (currEntry->flags & FSMEntryFlag::stateChangeOld) { + // Deplicated conditional expression. + currState = *((int*)(currEntry->offset)); + _LIBCXXABI_TRACE_STATETAB( + "Flag: FSMEntryFlag::stateChangeOld, 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 & FSMEntryFlag::stateChange) { + state->state = *((int*)(addr)); + _LIBCXXABI_TRACE_STATETAB( + "Flag: FSMEntryFlag::stateChange, 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->nextState; + } + _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) { + FSMEntry* currEntry = &fsm->table[state->state - 1]; + + if (currEntry->count == FSMEntryCount::terminate) { + _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 FSMEntryFlag::stateChange or FSMEntryFlag::stateChangeOld. + _LIBCXXABI_TRACE_STATETAB("Processing state=%d, flags=0x%hx\n", + state->state, currEntry->flags); + if (currEntry->flags & FSMEntryFlag::stateChangeOld) { + state->state = *((int*)(currEntry->offset)); + _LIBCXXABI_TRACE_STATETAB( + "Flag: FSMEntryFlag::stateChangeOld, 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 & FSMEntryFlag::stateChange) { + state->state = *((int*)(addr)); + _LIBCXXABI_TRACE_STATETAB( + "Flag: FSMEntryFlag::stateChange, 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 & FSMEntryFlag::vBaseFlag && + state->ignoreVBasePtrs) { + _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 == FSMEntryCount::deleteObject) { + _LIBCXXABI_TRACE_STATETAB("Delete object at %p\n", addr); + __Invoke__Delete(currEntry, addr); + } else if (currEntry->count == FSMEntryCount::beginCatch) { + // Branch to the landing pad to see if the exception can be + // caught. + _LIBCXXABI_TRACE_STATETAB("FSMEntryCount::beginCatch: handler " + "%p/%p, return _URC_HANDLER_FOUND\n", + currEntry->catchBlock, + *(void**)(currEntry->catchBlock)); + _LIBCXXABI_TRACE_STATETAB("Changed to state=%d\n", state->state); + state->state = currEntry->nextState; + results.landingPad = (uintptr_t) * (void**)(currEntry->catchBlock); + results.reason = _URC_HANDLER_FOUND; + return; + } else if (currEntry->count == FSMEntryCount::endCatch) { + // End current catch block and branch to the landing pad. + _LIBCXXABI_TRACE_STATETAB("FSMEntryCount::endCatch: handler " + "%p/%p, return _URC_HANDLER_FOUND\n", + currEntry->catchBlock, + *(void**)(currEntry->catchBlock)); + _LIBCXXABI_TRACE_STATETAB("Changed to state=%d\n", state->state); + state->state = currEntry->nextState; + results.landingPad = (uintptr_t) * (void**)(currEntry->catchBlock); + results.reason = _URC_HANDLER_FOUND; + return; + } else if (currEntry->count == FSMEntryCount::inlineDestructor) { + // Branch to user code to perform the action and eventually + // the inline unwinding will call _Unwind_resume. + _LIBCXXABI_TRACE_STATETAB("FSMEntryCount::inlineDestructor: handler " + "%p/%p, return _URC_HANDLER_FOUND\n", + currEntry->catchBlock, + *(void**)(currEntry->catchBlock)); + _LIBCXXABI_TRACE_STATETAB("Changed to state=%d\n", state->state); + state->state = currEntry->nextState; + results.landingPad = (uintptr_t) * (void**)(currEntry->catchBlock); + 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->nextState; + } + _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; +} + +// The following are EH helper functions for xlclang++ compiled code. + +// __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. +bool __xlc_catch_matchv2(_Unwind_Exception* exceptionObject, + std::type_info* catchTypeInfo, 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* throwTypeInfo = 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*>(catchTypeInfo); + __cxxabiv1::__shim_type_info* throwType = + static_cast<__cxxabiv1::__shim_type_info*>(throwTypeInfo); + _LIBCXXABI_TRACE_STATETAB("UnwindException=%p thrownObject=%p " + "throwTypeInfo=%p(%s) catchTypeInfo=%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; +} + +// __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. +void __xlc_throw_badexception() { + _LIBCXXABI_TRACE_STATETAB("Entering function: %s\n\n", __func__); + void* newexception = + new (__cxa_allocate_exception(sizeof(std::bad_exception))) + 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++. It aborts when a deleted virtual +// function is called. +void __Deleted_Virtual() { abort(); } + +// __catchThrownException is called during AIX library initialization and +// termination to handle exceptions. An implementation is also provided in +// libC.a(shrcore.o). This implementation is provided for applications that +// link with -lc++ (the xlclang++ or ibm-clang++ link default.) +int __catchThrownException( + void (*cdfunc)(void), // function which may fail + void (*cleanup)(void*), // cleanup function + void* cleanuparg, // parameter to cleanup function + int action) { // control exception throwing and termination + enum Action : int { None = 0, Rethrow = 1, Terminate = 2 }; + if (!cdfunc) + return 0; + if (action == Action::Rethrow && !cleanup) { + // No cleanup and rethrow is effectively no-op. + // Avoid the catch handler when possible to allow exceptions generated + // from xlC binaries to flow through. + (*cdfunc)(); + return 0; + } + try { + (*cdfunc)(); + } catch (...) { + if (action == Action::Terminate) + std::terminate(); + if (cleanup) + (*cleanup)(cleanuparg); + if (action == Action::Rethrow) + throw; + assert(action == Action::None); + return -1; // FAILED + } + return 0; +} +#endif // defined(_AIX) } // extern "C"