[libc++abi][AIX] add personality and helper functions for the state table EH
Summary:
This patch adds the personality and helper functions for the state table based EH used by IBM legacy compilers xlC and xlclang++ on AIX.
* A high level description of the state table based EH is provided in the code comments.
* Function scan_state_tab() is added to scan the state table. It is invoked by the state table personality routine __xlcxx_personality_v0() and returns scan_results like scan_eh_tab() does.
* A couple of EH helper functions used by xlC and xlclang++ generated code are also added, e.g., __xlc_catch_matchv2() which checks whether the thrown object matches the catch handler's exception type.
* Debugging macros _LIBCXXABI_TRACE_STATETAB, _LIBCXXABI_TRACE_STATETAB0, and _LIBCXXABI_TRACING_STATETAB are added to dump state table scanning traces if environment variable LIBCXXABI_PRINT_STATTAB is set.
* The state variable and state table data is the LSDA found from the traceback table of the function during unwinding.
Reviewed by: MaskRay, cebowleratibm, libc++abi
Differential Revision: https://reviews.llvm.org/D100504
GitOrigin-RevId: b8f5732634feaf7a9ce37d33f97f1fb490efeddd
diff --git a/src/aix_state_tab_eh.inc b/src/aix_state_tab_eh.inc
new file mode 100644
index 0000000..032a2c8
--- /dev/null
+++ b/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 <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
diff --git a/src/cxa_personality.cpp b/src/cxa_personality.cpp
index b7ff4c2..f58d4de 100644
--- a/src/cxa_personality.cpp
+++ b/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