mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-01-31 03:27:39 +00:00
8b33204f37
* third_party: Add libcxxabi Added libcxxabi from LLVM 17.0.6 The library implements the Itanium C++ exception handling ABI. * third_party/libcxxabi: Enable __cxa_thread_atexit Enable `__cxa_thread_atexit` from libcxxabi. `__cxa_thread_atexit_impl` is still implemented by the cosmo libc. The original `__cxa_thread_atexit` has been removed. * third_party/libcxx: Build with exceptions Build libcxx with exceptions enabled. - Removed `_LIBCPP_NO_EXCEPTIONS` from `__config`. - Switched the exception implementation to `libcxxabi`. These two files are taken from the same `libcxx` version as mentioned in `README.cosmo`. - Removed `new_handler_fallback` in favor of `libcxxabi` implementation. - Enable `-fexceptions` and `-frtti` for `libcxx`. - Removed `THIRD_PARTY_LIBCXX` dependency from `libcxxabi` and `libunwind`. These libraries do not use any runtime `libcxx` functions, just headers. * libc: Remove remaining redundant cxa functions - `__cxa_pure_virtual` in `libcxxabi` is also a stub similar to the existing one. - `__cxa_guard_*` from `libcxxabi` is used instead of the ones from Android. Now there should be no more duplicate implementations. `__cxa_thread_atexit_impl`, `__cxa_atexit`, and related supporting functions, are still left to other libraries as in `libcxxabi`. `libcxxabi` is also now added to `cosmopolitan.a` to make up for the removed functions. Affected in-tree libraries (`third_party/double-conversion`) have been updated.
745 lines
34 KiB
C++
745 lines
34 KiB
C++
//===----------------------------------------------------------------------===//
|
|
//
|
|
// 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 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.
|
|
_LIBCXXABI_FUNC_VIS _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) {
|
|
// Store the address of unwind_exception in the stack field
|
|
// reserved for compilers (SP + 3 * sizeof(uintptr_t)) in the stack of
|
|
// the caller of the function containing the landing pad (within the link
|
|
// area for the call to the latter) for __xlc_exception_handle()
|
|
// to retrieve when it is called by the landing pad.
|
|
uintptr_t *currentSP = reinterpret_cast<uintptr_t*>(_Unwind_GetGR(context, 1));
|
|
uintptr_t *callersSP = reinterpret_cast<uintptr_t*>(currentSP[0]);
|
|
callersSP[3] = reinterpret_cast<uintptr_t>(unwind_exception);
|
|
_LIBCXXABI_TRACE_STATETAB("Handshake: save unwind_exception=%p in stack=%p\n",
|
|
reinterpret_cast<void*>(unwind_exception), reinterpret_cast<void*>(callersSP));
|
|
// Jump to the handler.
|
|
_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.
|
|
_LIBCXXABI_FUNC_VIS 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.
|
|
_LIBCXXABI_FUNC_VIS 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);
|
|
}
|
|
|
|
// skip_non_cxx_eh_aware_frames
|
|
// This function skips non-C++ EH aware stack frames by unwinding from the
|
|
// stack frame pointed by 'Sp' and returns the first C++ EH aware stack frame
|
|
// found. 'Pc' is an instruction address inside the function that owns the
|
|
// stack frame pointed to by 'Sp'.
|
|
static uintptr_t* skip_non_cxx_eh_aware_frames(uint32_t* Pc, uintptr_t* Sp) {
|
|
uint32_t* currentPc = Pc;
|
|
uintptr_t* currentStack = Sp;
|
|
|
|
// Loop until a C++ EH aware frame is found or the return address is 0,
|
|
// which is the return address of the startup function '__start'.
|
|
while (currentPc != 0) {
|
|
uint32_t* p = currentPc;
|
|
|
|
// 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);
|
|
|
|
// A stack frame with a C++ state table is C++ EH aware.
|
|
if (TBTable->tb.lang == TB_CPLUSPLUS && TBTable->tb.has_ctl)
|
|
return currentStack;
|
|
|
|
// Move up one stack frame.
|
|
currentStack = reinterpret_cast<uintptr_t*>(currentStack[0]);
|
|
// Get the value of the LR (saved, prior to incrementing the SP, by the
|
|
// prolog of the function just inspected) from the frame.
|
|
currentPc = reinterpret_cast<uint32_t*>(currentStack[2]);
|
|
}
|
|
// This should not happen.
|
|
_LIBCXXABI_TRACE_STATETAB0("skip_non_cxx_eh_aware_frames() reached the end of stack frames, aborting\n");
|
|
abort();
|
|
}
|
|
|
|
// __xlc_exception_handle
|
|
// This function is for xlclang++. It returns the address of the exception
|
|
// object stored in the reserved field in the stack of the caller of the
|
|
// function that calls __xlc_exception_handle() (within the link area for the
|
|
// call to the latter). The address is stored by the personality routine for
|
|
// xlclang++ compiled code. If __xlc_exception_handle() is called by
|
|
// non-C++ EH aware functions, their frames are skipped until a C++ EH aware
|
|
// frame is found.
|
|
// Note: make sure __xlc_excpetion_handle() is a non-leaf function. Currently
|
|
// it calls skip_non_cxx_eh_aware_frames(), which in turn calls abort().
|
|
_LIBCXXABI_FUNC_VIS uintptr_t __xlc_exception_handle() {
|
|
// Get the SP of this function, i.e., __xlc_exception_handle().
|
|
uintptr_t* lastStack = reinterpret_cast<uintptr_t*>(__builtin_frame_address(0));
|
|
// Move one frame up to the frame of the caller of __xlc_exception_handle().
|
|
lastStack = reinterpret_cast<uintptr_t*>(lastStack[0]);
|
|
// Get the return address of this function, i.e., __xlc_exception_handle().
|
|
uint32_t* returnAddress = reinterpret_cast<uint32_t*>(__builtin_return_address(0));
|
|
|
|
// Skip non-C++ EH aware frames and get the first C++ EH aware frame.
|
|
uintptr_t* callerStack = skip_non_cxx_eh_aware_frames(returnAddress, lastStack);
|
|
|
|
// Get the SP of the caller of the C++ EH aware caller.
|
|
callerStack = reinterpret_cast<uintptr_t*>(callerStack[0]);
|
|
// Retrieve the exception object in the stack slot saved by the personality.
|
|
uintptr_t exceptionObject = callerStack[3];
|
|
_LIBCXXABI_TRACE_STATETAB("Handshake: retrieve exceptionObject=%p from stack=%p\n",
|
|
reinterpret_cast<void*>(exceptionObject), reinterpret_cast<void*>(callerStack));
|
|
return exceptionObject;
|
|
}
|
|
|
|
// xlclang++ may generate calls to __Deleted_Virtual.
|
|
_LIBCXXABI_FUNC_VIS 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.)
|
|
_LIBCXXABI_FUNC_VIS 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
|