| //===----------------------------------------------------------------------===// |
| // |
| // 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 <new> |
| #include <stdio.h> |
| #include <sys/debug.h> |
| |
| /* |
| 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<void*>(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<size_t>(fsmEntry->elementCount), fsmEntry->elemSize, |
| reinterpret_cast<destruct_f>(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<char**>(addr); |
| |
| _LIBCXXABI_TRACE_STATETAB("Delete object=%p, fsmEntry=%p\n", reinterpret_cast<void*>(objectAddress), |
| reinterpret_cast<void*>(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<uint32_t*>(_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<tbtable*>(p + 1); |
| |
| p = reinterpret_cast<uint32_t*>(&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<uint16_t*>(p); |
| p = reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(p) + name_len + sizeof(uint16_t)); |
| } |
| |
| if (TBTable->tb.uses_alloca) |
| framePointerReg = *reinterpret_cast<char*>(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<void*>(fsmEntry->address); |
| _LIBCXXABI_TRACE_STATETAB("Address calculation (global obj) addr=fsmEntry->address=%p\n", addr); |
| } else if (fsmEntry->flags & FSMEntryFlag::thisFlag) { |
| addr = reinterpret_cast<void*>(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<void*>( |
| *reinterpret_cast<char**>(get_frame_addr(context) + static_cast<uintptr_t>(fsmEntry->offset))); |
| _LIBCXXABI_TRACE_STATETAB("Address calculation (indirect obj) addr=%p, fsmEntry->offset=%ld \n", |
| addr, fsmEntry->offset); |
| } else { |
| addr = reinterpret_cast<void*>(get_frame_addr(context) + static_cast<uintptr_t>(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<void*>(unwind_exception), |
| reinterpret_cast<void*>(context)); |
| } |
| |
| // Start scan by getting state table address. |
| StateVariable* const state = reinterpret_cast<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; |
| 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<void*>(&currFSMEntry), i + 1, currFSMEntry->offset, |
| reinterpret_cast<void*>(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<int*>(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<int*>(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<int*>(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<int*>(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<void**>(currFSMEntry->landingPad)); |
| |
| state->state = currFSMEntry->nextState; |
| results.landingPad = reinterpret_cast<uintptr_t>(*reinterpret_cast<void**>(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<void*>(*reinterpret_cast<char**>(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<void*>(*reinterpret_cast<char**>(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<uintptr_t>(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<void*>(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<void*>(exceptionObject), |
| reinterpret_cast<void*>(&exceptionHeader->unwindHeader)); |
| exceptionObject = &exceptionHeader->unwindHeader; |
| } else { |
| _LIBCXXABI_TRACE_STATETAB("exceptionObject %p is NOT a dependent\n", reinterpret_cast<void*>(exceptionObject)); |
| exceptionHeader = reinterpret_cast<__cxa_exception*>(exceptionObject + 1) - 1; |
| } |
| |
| void* thrownObject = reinterpret_cast<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 = 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<void*>(exceptionObject), thrownObject, reinterpret_cast<void*>(throwType), |
| throwType->name(), reinterpret_cast<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_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<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() { |
| 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 |