746 lines · plain
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//8// This file implements the personality and helper functions for the state9// table based EH used by IBM legacy compilers xlC and xlclang++ on AIX.10//11//===----------------------------------------------------------------------===//12 13#include <new>14#include <stdio.h>15#include <sys/debug.h>16 17/*18 The legacy IBM xlC and xlclang++ compilers use the state table for EH19 instead of the range table. Destructors, or addresses of the possible catch20 sites or cleanup code are specified in the state table which is a finite21 state machine (FSM). Each function that has a state table also has an22 autolocal state variable. The state variable represents the current state23 of the function for EH and is found through the traceback table of the24 function during unwinding, which is located at the end of each function.25 The FSM is an array of state entries. Each state entry has the following26 fields:27 28 * offset/address/pointer - the offset used to locate the object, or the29 address of a global object, or the address of the next state if it is an30 old conditional state change entry;31 * dtor/landing pad - address of the destructor function to invoke,32 or address of the catch block or cleanup code in the user code to branch to;33 * element count/action flag - the number of elements or the flag for actions;34 * element size - if the object is an array this is the size of one element35 of the array;36 * flags - flags used to control how fields in the entry are interpreted;37 * next state - the state to execute next after the action for this state is38 performed. The value of zero indicates the end of the state for this39 function.40 41 The following is the description of 'element count/action flag' field.42+-----------------------------------------------------------------------------+43| value | description | action |44+-------+------------------------+--------------------------------------------+45| > 1 | object is an array | calls __cxa_vec_cleanup to run dtor for |46| | | each member of the array |47+-------+------------------------+--------------------------------------------+48| 1, 0 | object is a scalar | calls dtor for the object |49+-------+------------------------+--------------------------------------------+50| -1 | begin catch | branches to the handler which performes |51| | | catch-match. If there is no catch that |52| | | matches the exception it will be rethrown |53+-------+------------------------+--------------------------------------------+54| -2 | end catch | ends current catch block and continues |55| | | attempting to catch the exception |56+-------+------------------------+--------------------------------------------+57| -3 | delete the object | calls the delete function of the object |58+-------+------------------------+--------------------------------------------+59| -4 | cleanup label | branches to the user code for cleaning up |60+-------+------------------------+--------------------------------------------+61*/62 63namespace __cxxabiv1 {64 65extern "C" {66 67// Macros for debugging the state table parsing.68#ifdef NDEBUG69# define _LIBCXXABI_TRACE_STATETAB(msg, ...)70# define _LIBCXXABI_TRACE_STATETAB0(msg)71# define _LIBCXXABI_TRACE_STATETAB1(msg)72# define _LIBCXXABI_TRACING_STATETAB 073#else74static bool state_tab_dbg() {75 static bool checked = false;76 static bool log = false;77 if (!checked) {78 log = (getenv("LIBCXXABI_PRINT_STATTAB") != NULL);79 checked = true;80 }81 return log;82}83 84# define _LIBCXXABI_TRACE_STATETAB(msg, ...) \85 do { \86 if (state_tab_dbg()) \87 fprintf(stderr, "libcxxabi: " msg, __VA_ARGS__); \88 } while (0)89# define _LIBCXXABI_TRACE_STATETAB0(msg) \90 do { \91 if (state_tab_dbg()) \92 fprintf(stderr, "libcxxabi: " msg); \93 } while (0)94# define _LIBCXXABI_TRACE_STATETAB1(msg) \95 do { \96 if (state_tab_dbg()) \97 fprintf(stderr, msg); \98 } while (0)99 100# define _LIBCXXABI_TRACING_STATETAB state_tab_dbg()101#endif // NDEBUG102 103namespace __state_table_eh {104 105// Definition of flags for the state table entry field 'action flag'.106enum FSMEntryCount : intptr_t { beginCatch = -1, endCatch = -2, deleteObject = -3, cleanupLabel = -4, terminate = -5 };107 108// Definition of flags for the state table entry field 'flags'.109enum FSMEntryFlag : int16_t {110 indirect = 0x100, // Object was thrown from a function where111 // the return value optimization was used.112 oldConditionalStateChange = 0x400, // State table entry is an indirect state113 // change, dereference the address in114 // offset as int for the target state.115 // This is deprecated. This indicates116 // the address is direct. (static local).117 conditionalStateChange = 0x800, // State table entry is an indirect state118 // change, dereference the address in119 // offset as int for the target state.120 // The temporary is an automatic. State121 // change is used in cases such as122 // (b?(T1(),foo()):(T2(),foo())),throw 42;123 // which causes a conditional state change124 // so that we know if T1 or T2 need to be125 // destroyed.126 thisFlag = 0x01, // The address of the object for the127 // cleanup action is based on the128 // StateVariable::thisValue.129 vBaseFlag = 0x02, // The object is of a virtual base class.130 globalObj = 0x04 // FSMEntry::address is the address of131 // a global object.132};133 134namespace {135// The finite state machine to be walked.136struct FSMEntry {137 union {138 // Offset of the object within its stack frame or containing object.139 intptr_t offset;140 // Address of a global object.141 intptr_t address;142 // Address of the next state if it is an old conditional state change entry.143 intptr_t nextStatePtr;144 };145 union {146 // Address of the destructor function with 1 argument.147 void (*destructor)(void*);148 // Address of the destructor function with 2 arguments.149 void (*xlCDestructor)(void*, size_t);150 // The address of the catch block or cleanup code.151 void* landingPad;152 };153 union {154 // The flag for actions (when the value is negative).155 FSMEntryCount actionFlag;156 // The element count (when the value is positive or zero).157 size_t elementCount;158 };159 size_t elemSize;160 FSMEntryFlag flags;161 uint16_t nextState;162};163 164struct FSM {165 uint32_t magic; // Magic number of the state table.166 int32_t numberOfStates;167 FSMEntry table[1]; // Actually table[numberOfStates].168};169 170// The state variable on the stack.171struct StateVariable {172 int32_t state;173 struct FSM* table;174 intptr_t thisValue;175 int32_t ignoreVBasePtrs;176};177} // namespace178 179// State table magic number180enum FSMMagic : uint32_t {181 number = 0xbeefdead, // State table generated by xlC compiler.182 number2 = 0xbeeedead, // State table generated by early version xlC compiler.183 number3 = 0x1cedbeef // State table generated by xlclang++ compiler.184};185 186constexpr size_t dtorArgument = 0x02; // Flag to destructor indicating to free187 // virtual bases, don't delete object.188 189static void invoke_destructor(FSMEntry* fsmEntry, void* addr) {190 _LIBCXXABI_TRACE_STATETAB("Destruct object=%p, fsmEntry=%p\n", addr, reinterpret_cast<void*>(fsmEntry));191 try {192 if (fsmEntry->elementCount == 1) {193 _LIBCXXABI_TRACE_STATETAB0("calling scalar destructor\n");194 (*fsmEntry->xlCDestructor)(addr, dtorArgument);195 _LIBCXXABI_TRACE_STATETAB0("returned from scalar destructor\n");196 } else {197 _LIBCXXABI_TRACE_STATETAB0("calling vector destructor\n");198 __cxa_vec_cleanup(addr, reinterpret_cast<size_t>(fsmEntry->elementCount), fsmEntry->elemSize,199 fsmEntry->destructor);200 _LIBCXXABI_TRACE_STATETAB0("returned from vector destructor\n");201 }202 } catch (...) {203 _LIBCXXABI_TRACE_STATETAB0("Uncaught exception in destructor, terminating\n");204 std::terminate();205 }206}207 208static void invoke_delete(FSMEntry* fsmEntry, void* addr) {209 char* objectAddress = *reinterpret_cast<char**>(addr);210 211 _LIBCXXABI_TRACE_STATETAB("Delete object=%p, fsmEntry=%p\n", reinterpret_cast<void*>(objectAddress),212 reinterpret_cast<void*>(fsmEntry));213 try {214 _LIBCXXABI_TRACE_STATETAB0("..calling delete()\n");215 // 'destructor' holds a function pointer to delete().216 (*fsmEntry->xlCDestructor)(objectAddress, fsmEntry->elemSize);217 _LIBCXXABI_TRACE_STATETAB0("..returned from delete()\n");218 } catch (...) {219 _LIBCXXABI_TRACE_STATETAB0("Uncaught exception in delete(), terminating\n");220 std::terminate();221 }222}223 224// Get the frame address of the current function from its traceback table225// which is at the end of each function.226static uintptr_t get_frame_addr(_Unwind_Context* context) {227 int framePointerReg = 1; // default frame pointer == SP.228 uint32_t* p = reinterpret_cast<uint32_t*>(_Unwind_GetIP(context));229 230 // Keep looking forward until a word of 0 is found. The traceback231 // table starts at the following word.232 while (*p)233 ++p;234 tbtable* TBTable = reinterpret_cast<tbtable*>(p + 1);235 236 p = reinterpret_cast<uint32_t*>(&TBTable->tb_ext);237 238 // Skip field parminfo if it exists.239 if (TBTable->tb.fixedparms || TBTable->tb.floatparms)240 ++p;241 242 // Skip field tb_offset if it exists.243 if (TBTable->tb.has_tboff)244 ++p;245 246 // Skip field hand_mask if it exists.247 if (TBTable->tb.int_hndl)248 ++p;249 250 // Skip fields ctl_info and ctl_info_disp if they exist.251 if (TBTable->tb.has_ctl)252 p += 1 + *p;253 254 // Skip fields name_len and name if exist.255 if (TBTable->tb.name_present) {256 const uint16_t name_len = *reinterpret_cast<uint16_t*>(p);257 p = reinterpret_cast<uint32_t*>(reinterpret_cast<char*>(p) + name_len + sizeof(uint16_t));258 }259 260 if (TBTable->tb.uses_alloca)261 framePointerReg = *reinterpret_cast<char*>(p);262 263 return _Unwind_GetGR(context, framePointerReg);264}265 266// Calculate the object address from the FSM entry.267static void* compute_addr_from_table(FSMEntry* fsmEntry, StateVariable* const state, _Unwind_Context* context) {268 void* addr;269 if (fsmEntry->flags & FSMEntryFlag::globalObj) {270 addr = reinterpret_cast<void*>(fsmEntry->address);271 _LIBCXXABI_TRACE_STATETAB("Address calculation (global obj) addr=fsmEntry->address=%p\n", addr);272 } else if (fsmEntry->flags & FSMEntryFlag::thisFlag) {273 addr = reinterpret_cast<void*>(state->thisValue + fsmEntry->offset);274 _LIBCXXABI_TRACE_STATETAB("Address calculation (this obj) fsmEntry->offset=%ld : "275 "state->thisValue=%ld addr=(fsmEntry->offset+state->thisValue)=%p\n",276 fsmEntry->offset, state->thisValue, addr);277 } else if (fsmEntry->flags & FSMEntryFlag::indirect) {278 addr = reinterpret_cast<void*>(279 *reinterpret_cast<char**>(get_frame_addr(context) + static_cast<uintptr_t>(fsmEntry->offset)));280 _LIBCXXABI_TRACE_STATETAB("Address calculation (indirect obj) addr=%p, fsmEntry->offset=%ld \n",281 addr, fsmEntry->offset);282 } else {283 addr = reinterpret_cast<void*>(get_frame_addr(context) + static_cast<uintptr_t>(fsmEntry->offset));284 _LIBCXXABI_TRACE_STATETAB("Address calculation. (local obj) addr=fsmEntry->offset=%p\n",285 addr);286 }287 return addr;288}289 290static void scan_state_tab(scan_results& results, _Unwind_Action actions, bool native_exception,291 _Unwind_Exception* unwind_exception, _Unwind_Context* context) {292 // Initialize results to found nothing but an error.293 results.ttypeIndex = 0;294 results.actionRecord = 0;295 results.languageSpecificData = 0;296 results.landingPad = 0;297 results.adjustedPtr = 0;298 results.reason = _URC_FATAL_PHASE1_ERROR;299 300 // Check for consistent actions.301 if (actions & _UA_SEARCH_PHASE) {302 // Do Phase 1303 if (actions & (_UA_CLEANUP_PHASE | _UA_HANDLER_FRAME | _UA_FORCE_UNWIND)) {304 // None of these flags should be set during Phase 1.305 // Client error306 results.reason = _URC_FATAL_PHASE1_ERROR;307 return;308 }309 } else if (actions & _UA_CLEANUP_PHASE) {310 if ((actions & _UA_HANDLER_FRAME) && (actions & _UA_FORCE_UNWIND)) {311 // _UA_HANDLER_FRAME should only be set if phase 1 found a handler.312 // If _UA_FORCE_UNWIND is set, phase 1 shouldn't have happened.313 // Client error314 results.reason = _URC_FATAL_PHASE2_ERROR;315 return;316 }317 } else {318 // Neither _UA_SEARCH_PHASE nor _UA_CLEANUP_PHASE is set.319 // Client error320 results.reason = _URC_FATAL_PHASE1_ERROR;321 return;322 }323 324 if (_LIBCXXABI_TRACING_STATETAB) {325 _LIBCXXABI_TRACE_STATETAB1("\n");326 _LIBCXXABI_TRACE_STATETAB("%s: actions=%d (", __func__, actions);327 328 if (_UA_SEARCH_PHASE & actions)329 _LIBCXXABI_TRACE_STATETAB1("_UA_SEARCH_PHASE ");330 if (_UA_CLEANUP_PHASE & actions)331 _LIBCXXABI_TRACE_STATETAB1("_UA_CLEANUP_PHASE ");332 if (_UA_HANDLER_FRAME & actions)333 _LIBCXXABI_TRACE_STATETAB1("_UA_HANDLER_FRAME ");334 if (_UA_FORCE_UNWIND & actions)335 _LIBCXXABI_TRACE_STATETAB1("_UA_FORCE_UNWIND ");336 _LIBCXXABI_TRACE_STATETAB1(")\n");337 _LIBCXXABI_TRACE_STATETAB(" unwind_exception=%p context=%p\n", reinterpret_cast<void*>(unwind_exception),338 reinterpret_cast<void*>(context));339 }340 341 // Start scan by getting state table address.342 StateVariable* const state = reinterpret_cast<StateVariable* const>(_Unwind_GetLanguageSpecificData(context));343 if (state->state <= 0) {344 // The state is not correct - give up on this routine.345 _LIBCXXABI_TRACE_STATETAB("state=%d and is <= 0), continue unwinding\n", state->state);346 results.reason = _URC_CONTINUE_UNWIND;347 return;348 }349 // Parse the state table.350 FSM* const fsm = state->table;351 FSMEntry* currFSMEntry;352 353 if (fsm->magic != FSMMagic::number && fsm->magic != FSMMagic::number2 && fsm->magic != FSMMagic::number3) {354 // Something is wrong with the state table we found.355 if (_UA_SEARCH_PHASE & actions) {356 _LIBCXXABI_TRACE_STATETAB0("Invalid FSM table, return _URC_FATAL_PHASE1_ERROR\n");357 results.reason = _URC_FATAL_PHASE1_ERROR;358 } else if (_UA_CLEANUP_PHASE & actions) {359 _LIBCXXABI_TRACE_STATETAB0("Invalid FSM table, return _URC_FATAL_PHASE2_ERROR\n");360 results.reason = _URC_FATAL_PHASE2_ERROR;361 } else {362 // We should never get here.363 _LIBCXXABI_TRACE_STATETAB0("Invalid FSM table + RT Internal error, return _URC_FATAL_PHASE2_ERROR\n");364 results.reason = _URC_FATAL_PHASE2_ERROR;365 }366 return;367 }368 369 if (_LIBCXXABI_TRACING_STATETAB) {370 // Print the state table for debugging purposes.371 _LIBCXXABI_TRACE_STATETAB("state->state=%d, state->ignoreVBasePtrs=%d\n", state->state, state->ignoreVBasePtrs);372 _LIBCXXABI_TRACE_STATETAB("fsm->magic=%#x, fsm->numberOfStates=%d\n", fsm->magic, fsm->numberOfStates);373 // Print out the FSM table.374 _LIBCXXABI_TRACE_STATETAB0("FSM table:\n");375 _LIBCXXABI_TRACE_STATETAB("%12s %10s %8s %10s %7s %7s %7s %7s\n", "Entry Addr", "state", "Offset", "DTR/lpad",376 "count", "el_size", "flags", "next");377 for (int i = 0; i < fsm->numberOfStates; i++) {378 currFSMEntry = &fsm->table[i];379 _LIBCXXABI_TRACE_STATETAB("%12p (%8d) %8ld %10p %7ld "380 "%7ld %#7x %7d\n",381 reinterpret_cast<void*>(&currFSMEntry), i + 1, currFSMEntry->offset,382 reinterpret_cast<void*>(currFSMEntry->destructor),383 currFSMEntry->elementCount, currFSMEntry->elemSize, currFSMEntry->flags,384 currFSMEntry->nextState);385 }386 }387 388 if (_UA_SEARCH_PHASE & actions) {389 // Start walking the state table. Use a local copy of state->state so when390 // we return from search phase we don't change the state number.391 int currState = state->state;392 393 while (currState > 0) {394 currFSMEntry = &fsm->table[currState - 1];395 _LIBCXXABI_TRACE_STATETAB("Processing state=%d, flags=0x%hx\n", currState, currFSMEntry->flags);396 397 if (currFSMEntry->actionFlag == FSMEntryCount::beginCatch) {398 // Found a catch handler.399 if (fsm->magic == FSMMagic::number) {400 _LIBCXXABI_TRACE_STATETAB0("Found a xlC catch handler, return _URC_FATAL_PHASE1_ERROR\n");401 // xlC catch handlers cannot be entered because they use a402 // proprietary EH runtime that is not interoperable.403 results.reason = _URC_FATAL_PHASE1_ERROR;404 return;405 }406 // xlclang++ compiled frames use CXA-abi EH calls and any catch407 // block will include a catch(...) block so it is safe to assume that408 // the handler is found without checking the catch match. The409 // catch(...) block will rethrow the exception if there isn't a410 // match.411 _LIBCXXABI_TRACE_STATETAB0("Found a catch handler, return _URC_HANDLER_FOUND\n");412 results.reason = _URC_HANDLER_FOUND;413 return;414 }415 if (currFSMEntry->actionFlag == FSMEntryCount::terminate) {416 _LIBCXXABI_TRACE_STATETAB0("Found the terminate state, return _URC_HANDLER_FOUND\n");417 results.reason = _URC_HANDLER_FOUND;418 return;419 }420 if (currFSMEntry->flags & FSMEntryFlag::oldConditionalStateChange) {421 // Deprecated conditional expression.422 currState = *reinterpret_cast<int*>(currFSMEntry->nextStatePtr);423 _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::oldConditionalStateChange, dereference "424 "currFSMEntry->nextStatePtr(%ld), set state=%d\n",425 currFSMEntry->nextStatePtr, currState);426 continue; // We are done this iteration of the loop, since427 // we changed a state.428 }429 if (currFSMEntry->flags & FSMEntryFlag::conditionalStateChange) {430 void* addr = compute_addr_from_table(currFSMEntry, state, context);431 currState = *reinterpret_cast<int*>(addr);432 _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::conditionalStateChange, dereference "433 "addr(%p), set state=%d\n", addr, currState);434 continue; // We are done this iteration of the loop, since we435 // changed the state.436 }437 // Go to the next state.438 currState = currFSMEntry->nextState;439 }440 _LIBCXXABI_TRACE_STATETAB0("No catch handler found, return _URC_CONTINUE_UNWIND\n");441 results.reason = _URC_CONTINUE_UNWIND;442 return;443 }444 if (_UA_CLEANUP_PHASE & actions) {445 // Start walking the state table.446 while (state->state > 0) {447 currFSMEntry = &fsm->table[state->state - 1];448 449 if (currFSMEntry->actionFlag == FSMEntryCount::terminate) {450 _LIBCXXABI_TRACE_STATETAB0("Reached terminate state. Call terminate.\n");451 std::terminate();452 }453 // Perform action according to the currFSMEntry->actionFlag,454 // except when flag is FSMEntryFlag::conditionalStateChange or455 // FSMEntryFlag::oldConditionalStateChange.456 _LIBCXXABI_TRACE_STATETAB("Processing state=%d, flags=0x%hx\n", state->state, currFSMEntry->flags);457 if (currFSMEntry->flags & FSMEntryFlag::oldConditionalStateChange) {458 state->state = *reinterpret_cast<int*>(currFSMEntry->nextStatePtr);459 _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::oldConditionalStateChange, dereference "460 "currFSMEntry->nextStatePtr(%ld), set state=%d\n",461 currFSMEntry->nextStatePtr, state->state);462 continue; // We are done with this iteration of the loop, since we changed a state.463 }464 if (currFSMEntry->flags & FSMEntryFlag::conditionalStateChange) {465 // A conditional state table entry holds the address of a local466 // that holds the next state.467 void* addr = compute_addr_from_table(currFSMEntry, state, context);468 state->state = *reinterpret_cast<int*>(addr);469 _LIBCXXABI_TRACE_STATETAB("Flag: FSMEntryFlag::conditionalStateChange, dereference "470 "addr(%p), set state=%d\n", addr, state->state);471 continue; // We are done with this iteration of the loop, since we changed a state.472 }473 if (currFSMEntry->actionFlag == FSMEntryCount::beginCatch || currFSMEntry->actionFlag == FSMEntryCount::endCatch ||474 currFSMEntry->actionFlag == FSMEntryCount::cleanupLabel) {475 476 _LIBCXXABI_TRACE_STATETAB(477 "FSMEntryCount::%s: handler %p/%p, return _URC_HANDLER_FOUND\n",478 (currFSMEntry->actionFlag == FSMEntryCount::beginCatch479 ? "beginCatch"480 : (currFSMEntry->actionFlag == FSMEntryCount::endCatch ? "endCatch" : "cleanupLabel")),481 currFSMEntry->landingPad, *reinterpret_cast<void**>(currFSMEntry->landingPad));482 483 state->state = currFSMEntry->nextState;484 results.landingPad = reinterpret_cast<uintptr_t>(*reinterpret_cast<void**>(currFSMEntry->landingPad));485 results.reason = _URC_HANDLER_FOUND;486 return;487 }488 if (currFSMEntry->elementCount > 0) {489 if (currFSMEntry->flags & FSMEntryFlag::vBaseFlag && state->ignoreVBasePtrs) {490 _LIBCXXABI_TRACE_STATETAB0("Ignoring virtual base dtor.\n");491 } else {492 // We need to invoke the virtual base destructor. This must be493 // a frame from the legacy xlC compiler as the xlclang++ compiler494 // generates inline cleanup code rather than specifying495 // the destructor via the state table.496 void* addr = compute_addr_from_table(currFSMEntry, state, context);497 498 // An extra indirect to get to the object according to the object499 // model used by the xlC compiler.500 addr = reinterpret_cast<void*>(*reinterpret_cast<char**>(addr));501 _LIBCXXABI_TRACE_STATETAB("Invoke dtor for object=%p\n", addr);502 invoke_destructor(currFSMEntry, addr);503 }504 } else if (currFSMEntry->actionFlag == FSMEntryCount::deleteObject) {505 void* addr = compute_addr_from_table(currFSMEntry, state, context);506 if (currFSMEntry->flags & FSMEntryFlag::vBaseFlag) {507 // We need to invoke the virtual base delete function. This must be508 // a frame from the legacy xlC compiler as the xlclang++ compiler509 // generates inline cleanup code rather than specifying510 // the delete function via the state table.511 512 // An extra indirect to get to the object according to the object513 // model used by the xlC compiler.514 addr = reinterpret_cast<void*>(*reinterpret_cast<char**>(addr));515 }516 _LIBCXXABI_TRACE_STATETAB("Delete object at %p\n", addr);517 invoke_delete(currFSMEntry, addr);518 } else {519 _LIBCXXABI_TRACE_STATETAB("Unknown entry in FSM (count=%ld), ignored\n",520 currFSMEntry->elementCount);521 } // End of action switching.522 523 // Go to next state.524 state->state = currFSMEntry->nextState;525 }526 _LIBCXXABI_TRACE_STATETAB0("No catch handler, return _URC_CONTINUE_UNWIND\n");527 results.reason = _URC_CONTINUE_UNWIND;528 return;529 }530 _LIBCXXABI_TRACE_STATETAB0("No state table entry for this exception, call_terminate()\n");531 // It is possible that no state table entry specify how to handle532 // this exception. By spec, terminate it immediately.533 call_terminate(native_exception, unwind_exception);534}535 536// Personality routine for EH using the state table.537_LIBCXXABI_FUNC_VIS _Unwind_Reason_Code538__xlcxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClass,539 _Unwind_Exception* unwind_exception, _Unwind_Context* context) {540 if (version != 1 || unwind_exception == 0 || context == 0)541 return _URC_FATAL_PHASE1_ERROR;542 543 bool native_exception = (exceptionClass & get_vendor_and_language) == (kOurExceptionClass & get_vendor_and_language);544 scan_results results;545 scan_state_tab(results, actions, native_exception, unwind_exception, context);546 if (actions & _UA_SEARCH_PHASE) {547 // Phase 1 search: All we're looking for in phase 1 is a handler that548 // halts unwinding549 return results.reason;550 }551 if (actions & _UA_CLEANUP_PHASE) {552 // Phase 2 cleanup:553 if (results.reason == _URC_HANDLER_FOUND) {554 // Store the address of unwind_exception in the stack field555 // reserved for compilers (SP + 3 * sizeof(uintptr_t)) in the stack of556 // the caller of the function containing the landing pad (within the link557 // area for the call to the latter) for __xlc_exception_handle()558 // to retrieve when it is called by the landing pad.559 uintptr_t *currentSP = reinterpret_cast<uintptr_t*>(_Unwind_GetGR(context, 1));560 uintptr_t *callersSP = reinterpret_cast<uintptr_t*>(currentSP[0]);561 callersSP[3] = reinterpret_cast<uintptr_t>(unwind_exception);562 _LIBCXXABI_TRACE_STATETAB("Handshake: save unwind_exception=%p in stack=%p\n",563 reinterpret_cast<void*>(unwind_exception), reinterpret_cast<void*>(callersSP));564 // Jump to the handler.565 _Unwind_SetIP(context, results.landingPad);566 return _URC_INSTALL_CONTEXT;567 }568 // Did not find a handler. Return the results of the scan. Normally569 // _URC_CONTINUE_UNWIND, but could have been _URC_FATAL_PHASE2_ERROR.570 return results.reason;571 }572 // We were called improperly: neither a phase 1 or phase 2 search.573 return _URC_FATAL_PHASE1_ERROR;574}575} // namespace __state_table_eh576 577// The following are EH helper functions for xlclang++ compiled code.578 579// __xlc_catch_matchv2580// Check whether the thrown object matches the catch handler's exception581// declaration. If there is a match, the function returns true with adjusted582// address of the thrown object. Otherwise, returns false.583_LIBCXXABI_FUNC_VIS bool584__xlc_catch_matchv2(_Unwind_Exception* exceptionObject, std::type_info* catchTypeInfo, void*& obj) {585 _LIBCXXABI_TRACE_STATETAB("Entering %s, exceptionObject=%p\n", __func__, reinterpret_cast<void*>(exceptionObject));586 587 if (!__isOurExceptionClass(exceptionObject)) {588 _LIBCXXABI_TRACE_STATETAB0("No match, not a C++ exception\n");589 return false;590 }591 592 __cxa_exception* exceptionHeader = 0;593 594 if (__getExceptionClass(exceptionObject) == kOurDependentExceptionClass) {595 // Walk to the __cxa_dependent_exception primary exception for the596 // exception object and its type_info.597 __cxa_dependent_exception* dependentExceptionHeader =598 reinterpret_cast<__cxa_dependent_exception*>(exceptionObject + 1) - 1;599 exceptionHeader = reinterpret_cast<__cxa_exception*>(dependentExceptionHeader->primaryException) - 1;600 _LIBCXXABI_TRACE_STATETAB("exceptionObject 0x%p is a dependent, primary 0x%p\n",601 reinterpret_cast<void*>(exceptionObject),602 reinterpret_cast<void*>(&exceptionHeader->unwindHeader));603 exceptionObject = &exceptionHeader->unwindHeader;604 } else {605 _LIBCXXABI_TRACE_STATETAB("exceptionObject %p is NOT a dependent\n", reinterpret_cast<void*>(exceptionObject));606 exceptionHeader = reinterpret_cast<__cxa_exception*>(exceptionObject + 1) - 1;607 }608 609 void* thrownObject = reinterpret_cast<void*>(exceptionObject + 1);610 std::type_info* throwTypeInfo = exceptionHeader->exceptionType;611 612 // Get the type info for the thrown type and this catch clause and613 // see if the catch caluse can catch that type.614 615 __cxxabiv1::__shim_type_info* catchType = reinterpret_cast<__cxxabiv1::__shim_type_info*>(catchTypeInfo);616 __cxxabiv1::__shim_type_info* throwType = reinterpret_cast<__cxxabiv1::__shim_type_info*>(throwTypeInfo);617 _LIBCXXABI_TRACE_STATETAB("UnwindException=%p, thrownObject=%p, throwTypeInfo=%p(%s), catchTypeInfo=%p(%s)\n",618 reinterpret_cast<void*>(exceptionObject), thrownObject, reinterpret_cast<void*>(throwType),619 throwType->name(), reinterpret_cast<void*>(catchType), catchType->name());620 if (catchType->can_catch(throwType, thrownObject)) {621 exceptionHeader->adjustedPtr = thrownObject;622 obj = thrownObject;623 _LIBCXXABI_TRACE_STATETAB("Match found for thrownObject=%p\n", thrownObject);624 return true;625 }626 _LIBCXXABI_TRACE_STATETAB0("No match\n");627 return false;628}629 630// __xlc_throw_badexception631// This function is for xlclang++. It allocates and throws a bad_exception.632// During unwinding for this bad_exception, the previous exception which is633// not matching the throw spec will be cleaned up. Thus having the same634// effect as replace the top most exception (which is bad) with a bad_exception.635_LIBCXXABI_FUNC_VIS void __xlc_throw_badexception() {636 _LIBCXXABI_TRACE_STATETAB("Entering function: %s\n\n", __func__);637 void* newexception = new (__cxa_allocate_exception(sizeof(std::bad_exception))) std::bad_exception;638 __cxa_throw(newexception, const_cast<std::type_info*>(&typeid(std::bad_exception)), 0);639}640 641// skip_non_cxx_eh_aware_frames642// This function skips non-C++ EH aware stack frames by unwinding from the643// stack frame pointed by 'Sp' and returns the first C++ EH aware stack frame644// found. 'Pc' is an instruction address inside the function that owns the645// stack frame pointed to by 'Sp'.646static uintptr_t* skip_non_cxx_eh_aware_frames(uint32_t* Pc, uintptr_t* Sp) {647 uint32_t* currentPc = Pc;648 uintptr_t* currentStack = Sp;649 650 // Loop until a C++ EH aware frame is found or the return address is 0,651 // which is the return address of the startup function '__start'.652 while (currentPc != 0) {653 uint32_t* p = currentPc;654 655 // Keep looking forward until a word of 0 is found. The traceback656 // table starts at the following word.657 while (*p)658 ++p;659 tbtable* TBTable = reinterpret_cast<tbtable*>(p + 1);660 661 // A stack frame with a C++ state table is C++ EH aware.662 if (TBTable->tb.lang == TB_CPLUSPLUS && TBTable->tb.has_ctl)663 return currentStack;664 665 // Move up one stack frame.666 currentStack = reinterpret_cast<uintptr_t*>(currentStack[0]);667 // Get the value of the LR (saved, prior to incrementing the SP, by the668 // prolog of the function just inspected) from the frame.669 currentPc = reinterpret_cast<uint32_t*>(currentStack[2]);670 }671 // This should not happen.672 _LIBCXXABI_TRACE_STATETAB0("skip_non_cxx_eh_aware_frames() reached the end of stack frames, aborting\n");673 abort();674}675 676// __xlc_exception_handle677// This function is for xlclang++. It returns the address of the exception678// object stored in the reserved field in the stack of the caller of the679// function that calls __xlc_exception_handle() (within the link area for the680// call to the latter). The address is stored by the personality routine for681// xlclang++ compiled code. If __xlc_exception_handle() is called by682// non-C++ EH aware functions, their frames are skipped until a C++ EH aware683// frame is found.684// Note: make sure __xlc_exception_handle() is a non-leaf function. Currently685// it calls skip_non_cxx_eh_aware_frames(), which in turn calls abort().686_LIBCXXABI_FUNC_VIS uintptr_t __xlc_exception_handle() {687 // Get the SP of this function, i.e., __xlc_exception_handle().688 uintptr_t* lastStack = reinterpret_cast<uintptr_t*>(__builtin_frame_address(0));689 // Move one frame up to the frame of the caller of __xlc_exception_handle().690 lastStack = reinterpret_cast<uintptr_t*>(lastStack[0]);691 // Get the return address of this function, i.e., __xlc_exception_handle().692 uint32_t* returnAddress = reinterpret_cast<uint32_t*>(__builtin_return_address(0));693 694 // Skip non-C++ EH aware frames and get the first C++ EH aware frame.695 uintptr_t* callerStack = skip_non_cxx_eh_aware_frames(returnAddress, lastStack);696 697 // Get the SP of the caller of the C++ EH aware caller.698 callerStack = reinterpret_cast<uintptr_t*>(callerStack[0]);699 // Retrieve the exception object in the stack slot saved by the personality.700 uintptr_t exceptionObject = callerStack[3];701 _LIBCXXABI_TRACE_STATETAB("Handshake: retrieve exceptionObject=%p from stack=%p\n",702 reinterpret_cast<void*>(exceptionObject), reinterpret_cast<void*>(callerStack));703 return exceptionObject;704}705 706// xlclang++ may generate calls to __Deleted_Virtual.707_LIBCXXABI_FUNC_VIS void __Deleted_Virtual() { abort(); }708 709// __catchThrownException is called during AIX library initialization and710// termination to handle exceptions. An implementation is also provided in711// libC.a(shrcore.o). This implementation is provided for applications that712// link with -lc++ (the xlclang++ or ibm-clang++ link default.)713_LIBCXXABI_FUNC_VIS int714__catchThrownException(void (*cdfunc)(void), // function which may fail715 void (*cleanup)(void*), // cleanup function716 void* cleanuparg, // parameter to cleanup function717 int action) { // control exception throwing and termination718 enum Action : int { None = 0, Rethrow = 1, Terminate = 2 };719 if (!cdfunc)720 return 0;721 if (action == Action::Rethrow && !cleanup) {722 // No cleanup and rethrow is effectively no-op.723 // Avoid the catch handler when possible to allow exceptions generated724 // from xlC binaries to flow through.725 (*cdfunc)();726 return 0;727 }728 try {729 (*cdfunc)();730 } catch (...) {731 if (action == Action::Terminate)732 std::terminate();733 if (cleanup)734 (*cleanup)(cleanuparg);735 if (action == Action::Rethrow)736 throw;737 assert(action == Action::None);738 return -1; // FAILED739 }740 return 0;741}742 743} // extern "C"744 745} // __cxxabiv1746