brintos

brintos / llvm-project-archived public Read only

0
0
Text · 34.4 KiB · 0ed3298 Raw
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