diff --git a/libcxxabi/src/aix_state_tab_eh.inc b/libcxxabi/src/aix_state_tab_eh.inc new file mode 100644 --- /dev/null +++ b/libcxxabi/src/aix_state_tab_eh.inc @@ -0,0 +1,681 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// +// This file implements the personality and helper functions for the state +// table based EH used by IBM legacy compilers xlC and xlclang++ on AIX. +// +//===----------------------------------------------------------------------===// + +#include +#include +#include + +/* + The legacy IBM xlC and xlclang++ compilers use the state table for EH + instead of the range table. Destructors, or addresses of the possible catch + sites or cleanup code are specified in the state table which is a finite + state machine (FSM). Each function that has a state table also has an + autolocal state variable. The state variable represents the current state + of the function for EH and is found through the traceback table of the + function during unwinding, which is located at the end of each function. + The FSM is an array of state entries. Each state entry has the following + fields: + + * offset/address/pointer - the offset used to locate the object, or the + address of a global object, or the address of the next state if it is an + old conditional state change entry; + * dtor/landing pad - address of the destructor function to invoke, + or address of the catch block or cleanup code in the user code to branch to; + * element count/action flag - the number of elements or the flag for actions; + * element size - if the object is an array this is the size of one element + of the array; + * flags - flags used to control how fields in the entry are interpreted; + * next state - the state to execute next after the action for this state is + performed. The value of zero indicates the end of the state for this + function. + + The following is the description of 'element count/action flag' field. ++-----------------------------------------------------------------------------+ +| value | description | action | ++-------+------------------------+--------------------------------------------+ +| > 1 | object is an array | calls __cxa_vec_cleanup to run dtor for | +| | | each member of the array | ++-------+------------------------+--------------------------------------------+ +| 1, 0 | object is a scalar | calls dtor for the object | ++-------+------------------------+--------------------------------------------+ +| -1 | begin catch | branches to the handler which performes | +| | | catch-match. If there is no catch that | +| | | matches the exception it will be rethrown | ++-------+------------------------+--------------------------------------------+ +| -2 | end catch | ends current catch block and continues | +| | | attempting to catch the exception | ++-------+------------------------+--------------------------------------------+ +| -3 | delete the object | calls the delete function of the object | ++-------+------------------------+--------------------------------------------+ +| -4 | cleanup label | branches to the user code for cleaning up | ++-------+------------------------+--------------------------------------------+ +*/ + +namespace __cxxabiv1 { + +extern "C" { + +// Macros for debugging the state table parsing. +#ifdef NDEBUG +# define _LIBCXXABI_TRACE_STATETAB(msg, ...) +# define _LIBCXXABI_TRACE_STATETAB0(msg) +# define _LIBCXXABI_TRACE_STATETAB1(msg) +# define _LIBCXXABI_TRACING_STATETAB 0 +#else +static bool state_tab_dbg() { + static bool checked = false; + static bool log = false; + if (!checked) { + log = (getenv("LIBCXXABI_PRINT_STATTAB") != NULL); + checked = true; + } + return log; +} + +# define _LIBCXXABI_TRACE_STATETAB(msg, ...) \ + do { \ + if (state_tab_dbg()) \ + fprintf(stderr, "libcxxabi: " msg, __VA_ARGS__); \ + } while (0) +# define _LIBCXXABI_TRACE_STATETAB0(msg) \ + do { \ + if (state_tab_dbg()) \ + fprintf(stderr, "libcxxabi: " msg); \ + } while (0) +# define _LIBCXXABI_TRACE_STATETAB1(msg) \ + do { \ + if (state_tab_dbg()) \ + fprintf(stderr, msg); \ + } while (0) + +# define _LIBCXXABI_TRACING_STATETAB state_tab_dbg() +#endif // NDEBUG + +namespace __state_table_eh { + +using destruct_f = void (*)(void*); + +// Definition of flags for the state table entry field 'action flag'. +enum FSMEntryCount : intptr_t { beginCatch = -1, endCatch = -2, deleteObject = -3, cleanupLabel = -4, terminate = -5 }; + +// Definition of flags for the state table entry field 'flags'. +enum FSMEntryFlag : int16_t { + indirect = 0x100, // Object was thrown from a function where + // the return value optimization was used. + oldConditionalStateChange = 0x400, // State table entry is an indirect state + // change, dereference the address in + // offset as int for the target state. + // This is deprecated. This indicates + // the address is direct. (static local). + conditionalStateChange = 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. State + // change is used in cases such as + // (b?(T1(),foo()):(T2(),foo())),throw 42; + // which causes a conditional state change + // so that we know if T1 or T2 need to be + // destroyed. + thisFlag = 0x01, // The address of the object for the + // cleanup action is based on the + // StateVariable::thisValue. + vBaseFlag = 0x02, // The object is of a virtual base class. + globalObj = 0x04 // FSMEntry::address is the address of + // a global object. +}; + +namespace { +// The finite state machine to be walked. +struct FSMEntry { + union { + // Offset of the object within its stack frame or containing object. + intptr_t offset; + // Address of a global object. + intptr_t address; + // Address of the next state if it is an old conditional state change entry. + intptr_t nextStatePtr; + }; + union { + // Address of the destructor function. + void (*destructor)(void*, size_t); + // The address of the catch block or cleanup code. + void* landingPad; + }; + union { + // The flag for actions (when the value is negative). + FSMEntryCount actionFlag; + // The element count (when the value is positive or zero). + size_t elementCount; + }; + size_t elemSize; + FSMEntryFlag flags; + uint16_t nextState; +}; + +struct FSM { + uint32_t magic; // Magic number of the state table. + int32_t numberOfStates; + FSMEntry table[1]; // Actually table[numberOfStates]. +}; + +// The state variable on the stack. +struct StateVariable { + int32_t state; + struct FSM* table; + intptr_t thisValue; + int32_t ignoreVBasePtrs; +}; +} // namespace + +// State table magic number +enum FSMMagic : uint32_t { + number = 0xbeefdead, // State table generated by xlC compiler. + number2 = 0xbeeedead, // State table generated by early version xlC compiler. + number3 = 0x1cedbeef // State table generated by xlclang++ compiler. +}; + +constexpr uint32_t REG_EXCP_OBJ = 14; // Register to pass the address of the exception + // object from the personality to xlclang++ + // compiled code. + +constexpr size_t dtorArgument = 0x02; // Flag to destructor indicating to free + // virtual bases, don't delete object. + +static void invoke_destructor(FSMEntry* fsmEntry, void* addr) { + _LIBCXXABI_TRACE_STATETAB("Destruct object=%p, fsmEntry=%p\n", addr, reinterpret_cast(fsmEntry)); + try { + if (fsmEntry->elementCount == 1) { + _LIBCXXABI_TRACE_STATETAB0("calling scalar destructor\n"); + (*fsmEntry->destructor)(addr, dtorArgument); + _LIBCXXABI_TRACE_STATETAB0("returned from scalar destructor\n"); + } else { + _LIBCXXABI_TRACE_STATETAB0("calling vector destructor\n"); + __cxa_vec_cleanup(addr, reinterpret_cast(fsmEntry->elementCount), fsmEntry->elemSize, + reinterpret_cast(fsmEntry->destructor)); + _LIBCXXABI_TRACE_STATETAB0("returned from vector destructor\n"); + } + } catch (...) { + _LIBCXXABI_TRACE_STATETAB0("Uncaught exception in destructor, terminating\n"); + std::terminate(); + } +} + +static void invoke_delete(FSMEntry* fsmEntry, void* addr) { + char* objectAddress = *reinterpret_cast(addr); + + _LIBCXXABI_TRACE_STATETAB("Delete object=%p, fsmEntry=%p\n", reinterpret_cast(objectAddress), + reinterpret_cast(fsmEntry)); + try { + _LIBCXXABI_TRACE_STATETAB0("..calling delete()\n"); + // 'destructor' holds a function pointer to delete(). + (*fsmEntry->destructor)(objectAddress, fsmEntry->elemSize); + _LIBCXXABI_TRACE_STATETAB0("..returned from delete()\n"); + } catch (...) { + _LIBCXXABI_TRACE_STATETAB0("Uncaught exception in delete(), terminating\n"); + std::terminate(); + } +} + +// Get the frame address of the current function from its traceback table +// which is at the end of each function. +static uintptr_t get_frame_addr(_Unwind_Context* context) { + int framePointerReg = 1; // default frame pointer == SP. + uint32_t* p = reinterpret_cast(_Unwind_GetIP(context)); + + // Keep looking forward until a word of 0 is found. The traceback + // table starts at the following word. + while (*p) + ++p; + tbtable* TBTable = reinterpret_cast(p + 1); + + p = reinterpret_cast(&TBTable->tb_ext); + + // Skip field parminfo if it exists. + if (TBTable->tb.fixedparms || TBTable->tb.floatparms) + ++p; + + // Skip field tb_offset if it exists. + if (TBTable->tb.has_tboff) + ++p; + + // Skip field hand_mask if it exists. + if (TBTable->tb.int_hndl) + ++p; + + // Skip fields ctl_info and ctl_info_disp if they exist. + if (TBTable->tb.has_ctl) + p += 1 + *p; + + // Skip fields name_len and name if exist. + if (TBTable->tb.name_present) { + const uint16_t name_len = *reinterpret_cast(p); + p = reinterpret_cast(reinterpret_cast(p) + name_len + sizeof(uint16_t)); + } + + if (TBTable->tb.uses_alloca) + framePointerReg = *reinterpret_cast(p); + + return _Unwind_GetGR(context, framePointerReg); +} + +// Calculate the object address from the FSM entry. +static void* compute_addr_from_table(FSMEntry* fsmEntry, StateVariable* const state, _Unwind_Context* context) { + void* addr; + if (fsmEntry->flags & FSMEntryFlag::globalObj) { + addr = reinterpret_cast(fsmEntry->address); + _LIBCXXABI_TRACE_STATETAB("Address calculation (global obj) addr=fsmEntry->address=%p\n", addr); + } else if (fsmEntry->flags & FSMEntryFlag::thisFlag) { + addr = reinterpret_cast(state->thisValue + fsmEntry->offset); + _LIBCXXABI_TRACE_STATETAB("Address calculation (this obj) fsmEntry->offset=%ld : " + "state->thisValue=%ld addr=(fsmEntry->offset+state->thisValue)=%p\n", + fsmEntry->offset, state->thisValue, addr); + } else if (fsmEntry->flags & FSMEntryFlag::indirect) { + addr = reinterpret_cast( + *reinterpret_cast(get_frame_addr(context) + static_cast(fsmEntry->offset))); + _LIBCXXABI_TRACE_STATETAB("Address calculation (indirect obj) addr=%p, fsmEntry->offset=%ld \n", + addr, fsmEntry->offset); + } else { + addr = reinterpret_cast(get_frame_addr(context) + static_cast(fsmEntry->offset)); + _LIBCXXABI_TRACE_STATETAB("Address calculation. (local obj) addr=fsmEntry->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_STATETAB1("\n"); + _LIBCXXABI_TRACE_STATETAB("%s: actions=%d (", __func__, actions); + + if (_UA_SEARCH_PHASE & actions) + _LIBCXXABI_TRACE_STATETAB1("_UA_SEARCH_PHASE "); + if (_UA_CLEANUP_PHASE & actions) + _LIBCXXABI_TRACE_STATETAB1("_UA_CLEANUP_PHASE "); + if (_UA_HANDLER_FRAME & actions) + _LIBCXXABI_TRACE_STATETAB1("_UA_HANDLER_FRAME "); + if (_UA_FORCE_UNWIND & actions) + _LIBCXXABI_TRACE_STATETAB1("_UA_FORCE_UNWIND "); + _LIBCXXABI_TRACE_STATETAB1(")\n"); + _LIBCXXABI_TRACE_STATETAB(" unwind_exception=%p context=%p\n", reinterpret_cast(unwind_exception), + reinterpret_cast(context)); + } + + // Start scan by getting state table address. + StateVariable* const state = reinterpret_cast(_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; + FSMEntry* currFSMEntry; + + if (fsm->magic != FSMMagic::number && fsm->magic != FSMMagic::number2 && fsm->magic != FSMMagic::number3) { + // Something is wrong with the state table we found. + if (_UA_SEARCH_PHASE & actions) { + _LIBCXXABI_TRACE_STATETAB0("Invalid FSM table, return _URC_FATAL_PHASE1_ERROR\n"); + results.reason = _URC_FATAL_PHASE1_ERROR; + } else if (_UA_CLEANUP_PHASE & actions) { + _LIBCXXABI_TRACE_STATETAB0("Invalid FSM table, return _URC_FATAL_PHASE2_ERROR\n"); + results.reason = _URC_FATAL_PHASE2_ERROR; + } else { + // We should never get here. + _LIBCXXABI_TRACE_STATETAB0("Invalid FSM table + RT Internal error, return _URC_FATAL_PHASE2_ERROR\n"); + results.reason = _URC_FATAL_PHASE2_ERROR; + } + return; + } + + if (_LIBCXXABI_TRACING_STATETAB) { + // Print the state table for debugging purposes. + _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); + // Print out the FSM table. + _LIBCXXABI_TRACE_STATETAB0("FSM table:\n"); + _LIBCXXABI_TRACE_STATETAB("%12s %10s %8s %10s %7s %7s %7s %7s\n", "Entry Addr", "state", "Offset", "DTR/lpad", + "count", "el_size", "flags", "next"); + for (int i = 0; i < fsm->numberOfStates; i++) { + currFSMEntry = &fsm->table[i]; + _LIBCXXABI_TRACE_STATETAB("%12p (%8d) %8ld %10p %7ld " + "%7ld %#7x %7d\n", + reinterpret_cast(&currFSMEntry), i + 1, currFSMEntry->offset, + reinterpret_cast(currFSMEntry->destructor), + currFSMEntry->elementCount, currFSMEntry->elemSize, currFSMEntry->flags, + currFSMEntry->nextState); + } + } + + if (_UA_SEARCH_PHASE & actions) { + // Start walking the state 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) { + currFSMEntry = &fsm->table[currState - 1]; + _LIBCXXABI_TRACE_STATETAB("Processing state=%d, flags=0x%hx\n", currState, currFSMEntry->flags); + + if (currFSMEntry->actionFlag == FSMEntryCount::beginCatch) { + // Found a catch handler. + if (fsm->magic == FSMMagic::number) { + _LIBCXXABI_TRACE_STATETAB0("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; + } + // xlclang++ compiled frames use CXA-abi EH calls and any catch + // block will include a catch(...) block so it is safe to assume that + // the handler is found without checking the catch match. The + // catch(...) block will rethrow the exception if there isn't a + // match. + _LIBCXXABI_TRACE_STATETAB0("Found a catch handler, return _URC_HANDLER_FOUND\n"); + results.reason = _URC_HANDLER_FOUND; + return; + } + if (currFSMEntry->actionFlag == FSMEntryCount::terminate) { + _LIBCXXABI_TRACE_STATETAB0("Found the terminate state, return _URC_HANDLER_FOUND\n"); + results.reason = _URC_HANDLER_FOUND; + return; + } + if (currFSMEntry->flags & FSMEntryFlag::oldConditionalStateChange) { + // Deprecated conditional expression. + currState = *reinterpret_cast(currFSMEntry->nextStatePtr); + _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::oldConditionalStateChange, dereference " + "currFSMEntry->nextStatePtr(%ld), set state=%d\n", + currFSMEntry->nextStatePtr, currState); + continue; // We are done this iteration of the loop, since + // we changed a state. + } + if (currFSMEntry->flags & FSMEntryFlag::conditionalStateChange) { + void* addr = compute_addr_from_table(currFSMEntry, state, context); + currState = *reinterpret_cast(addr); + _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::conditionalStateChange, dereference " + "addr(%p), set state=%d\n", addr, currState); + continue; // We are done this iteration of the loop, since we + // changed the state. + } + // Go to the next state. + currState = currFSMEntry->nextState; + } + _LIBCXXABI_TRACE_STATETAB0("No catch handler found, return _URC_CONTINUE_UNWIND\n"); + results.reason = _URC_CONTINUE_UNWIND; + return; + } + if (_UA_CLEANUP_PHASE & actions) { + // Start walking the state table. + while (state->state > 0) { + currFSMEntry = &fsm->table[state->state - 1]; + + if (currFSMEntry->actionFlag == FSMEntryCount::terminate) { + _LIBCXXABI_TRACE_STATETAB0("Reached terminate state. Call terminate.\n"); + std::terminate(); + } + // Perform action according to the currFSMEntry->actionFlag, + // except when flag is FSMEntryFlag::conditionalStateChange or + // FSMEntryFlag::oldConditionalStateChange. + _LIBCXXABI_TRACE_STATETAB("Processing state=%d, flags=0x%hx\n", state->state, currFSMEntry->flags); + if (currFSMEntry->flags & FSMEntryFlag::oldConditionalStateChange) { + state->state = *reinterpret_cast(currFSMEntry->nextStatePtr); + _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::oldConditionalStateChange, dereference " + "currFSMEntry->nextStatePtr(%ld), set state=%d\n", + currFSMEntry->nextStatePtr, state->state); + continue; // We are done with this iteration of the loop, since we changed a state. + } + if (currFSMEntry->flags & FSMEntryFlag::conditionalStateChange) { + // A conditional state table entry holds the address of a local + // that holds the next state. + void* addr = compute_addr_from_table(currFSMEntry, state, context); + state->state = *reinterpret_cast(addr); + _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::conditionalStateChange, 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. + } + if (currFSMEntry->actionFlag == FSMEntryCount::beginCatch || currFSMEntry->actionFlag == FSMEntryCount::endCatch || + currFSMEntry->actionFlag == FSMEntryCount::cleanupLabel) { + + _LIBCXXABI_TRACE_STATETAB( + "FSMEntryCount::%s: handler %p/%p, return _URC_HANDLER_FOUND\n", + (currFSMEntry->actionFlag == FSMEntryCount::beginCatch + ? "beginCatch" + : (currFSMEntry->actionFlag == FSMEntryCount::endCatch ? "endCatch" : "cleanupLabel")), + currFSMEntry->landingPad, *reinterpret_cast(currFSMEntry->landingPad)); + + state->state = currFSMEntry->nextState; + results.landingPad = reinterpret_cast(*reinterpret_cast(currFSMEntry->landingPad)); + results.reason = _URC_HANDLER_FOUND; + return; + } + if (currFSMEntry->elementCount > 0) { + if (currFSMEntry->flags & FSMEntryFlag::vBaseFlag && state->ignoreVBasePtrs) { + _LIBCXXABI_TRACE_STATETAB0("Ignoring virtual base dtor.\n"); + } else { + // We need to invoke the virtual base destructor. This must be + // a frame from the legacy xlC compiler as the xlclang++ compiler + // generates inline cleanup code rather than specifying + // the destructor via the state table. + void* addr = compute_addr_from_table(currFSMEntry, state, context); + + // An extra indirect to get to the object according to the object + // model used by the xlC compiler. + addr = reinterpret_cast(*reinterpret_cast(addr)); + _LIBCXXABI_TRACE_STATETAB("Invoke dtor for object=%p\n", addr); + invoke_destructor(currFSMEntry, addr); + } + } else if (currFSMEntry->actionFlag == FSMEntryCount::deleteObject) { + void* addr = compute_addr_from_table(currFSMEntry, state, context); + if (currFSMEntry->flags & FSMEntryFlag::vBaseFlag) { + // We need to invoke the virtual base delete function. This must be + // a frame from the legacy xlC compiler as the xlclang++ compiler + // generates inline cleanup code rather than specifying + // the delete function via the state table. + + // An extra indirect to get to the object according to the object + // model used by the xlC compiler. + addr = reinterpret_cast(*reinterpret_cast(addr)); + } + _LIBCXXABI_TRACE_STATETAB("Delete object at %p\n", addr); + invoke_delete(currFSMEntry, addr); + } else { + _LIBCXXABI_TRACE_STATETAB("Unknown entry in FSM (count=%ld), ignored\n", + currFSMEntry->elementCount); + } // End of action switching. + + // Go to next state. + state->state = currFSMEntry->nextState; + } + _LIBCXXABI_TRACE_STATETAB0("No catch handler, return _URC_CONTINUE_UNWIND\n"); + results.reason = _URC_CONTINUE_UNWIND; + return; + } + _LIBCXXABI_TRACE_STATETAB0("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 the 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, REG_EXCP_OBJ, 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; +} +} // namespace __state_table_eh + +// 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, returns false. +bool __xlc_catch_matchv2(_Unwind_Exception* exceptionObject, std::type_info* catchTypeInfo, void*& obj) { + _LIBCXXABI_TRACE_STATETAB("Entering %s, exceptionObject=%p\n", __func__, reinterpret_cast(exceptionObject)); + + if (!__isOurExceptionClass(exceptionObject)) { + _LIBCXXABI_TRACE_STATETAB0("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 = + reinterpret_cast<__cxa_dependent_exception*>(exceptionObject + 1) - 1; + exceptionHeader = reinterpret_cast<__cxa_exception*>(dependentExceptionHeader->primaryException) - 1; + _LIBCXXABI_TRACE_STATETAB("exceptionObject 0x%p is a dependent, primary 0x%p\n", + reinterpret_cast(exceptionObject), + reinterpret_cast(&exceptionHeader->unwindHeader)); + exceptionObject = &exceptionHeader->unwindHeader; + } else { + _LIBCXXABI_TRACE_STATETAB("exceptionObject %p is NOT a dependent\n", reinterpret_cast(exceptionObject)); + exceptionHeader = reinterpret_cast<__cxa_exception*>(exceptionObject + 1) - 1; + } + + void* thrownObject = reinterpret_cast(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 = reinterpret_cast<__cxxabiv1::__shim_type_info*>(catchTypeInfo); + __cxxabiv1::__shim_type_info* throwType = reinterpret_cast<__cxxabiv1::__shim_type_info*>(throwTypeInfo); + _LIBCXXABI_TRACE_STATETAB("UnwindException=%p, thrownObject=%p, throwTypeInfo=%p(%s), catchTypeInfo=%p(%s)\n", + reinterpret_cast(exceptionObject), thrownObject, reinterpret_cast(throwType), + throwType->name(), reinterpret_cast(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_STATETAB0("No match\n"); + return false; +} + +// __xlc_throw_badexception +// This function is for xlclang++. It allocates and throws a bad_exception. +// During unwinding for this bad_exception, 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 bad_exception. +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, const_cast(&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() { + uintptr_t exceptionObject; + asm("mr %0, 14" : "=r"(exceptionObject)); + return exceptionObject; +} + +// xlclang++ may generate calls to __Deleted_Virtual. +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; +} + +} // extern "C" + +} // __cxxabiv1 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 @@ -1305,3 +1305,9 @@ } // extern "C" } // __cxxabiv1 + +#if defined(_AIX) +// Include implementation of the personality and helper functions for the +// state table based EH used by IBM legacy compilers xlC and xlclang++ on AIX. +# include "aix_state_tab_eh.inc" +#endif