diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp --- a/libcxxabi/src/cxa_personality.cpp +++ b/libcxxabi/src/cxa_personality.cpp @@ -241,74 +241,75 @@ /// @link http://dwarfstd.org/Dwarf3.pdf @unlink /// @param data reference variable holding memory pointer to decode from /// @param encoding dwarf encoding type +/// @param base for adding relative offset, default to 0 /// @returns decoded value -static -uintptr_t -readEncodedPointer(const uint8_t** data, uint8_t encoding) -{ - uintptr_t result = 0; - if (encoding == DW_EH_PE_omit) - return result; - const uint8_t* p = *data; - // first get value - switch (encoding & 0x0F) - { - case DW_EH_PE_absptr: - result = readPointerHelper(p); - break; - case DW_EH_PE_uleb128: - result = readULEB128(&p); - break; - case DW_EH_PE_sleb128: - result = static_cast(readSLEB128(&p)); - break; - case DW_EH_PE_udata2: - result = readPointerHelper(p); - break; - case DW_EH_PE_udata4: - result = readPointerHelper(p); - break; - case DW_EH_PE_udata8: - result = readPointerHelper(p); - break; - case DW_EH_PE_sdata2: - result = readPointerHelper(p); - break; - case DW_EH_PE_sdata4: - result = readPointerHelper(p); - break; - case DW_EH_PE_sdata8: - result = readPointerHelper(p); - break; - default: - // not supported - abort(); - break; - } - // then add relative offset - switch (encoding & 0x70) - { - case DW_EH_PE_absptr: - // do nothing - break; - case DW_EH_PE_pcrel: - if (result) - result += (uintptr_t)(*data); - break; - case DW_EH_PE_textrel: - case DW_EH_PE_datarel: - case DW_EH_PE_funcrel: - case DW_EH_PE_aligned: - default: - // not supported - abort(); - break; - } - // then apply indirection - if (result && (encoding & DW_EH_PE_indirect)) - result = *((uintptr_t*)result); - *data = p; +static uintptr_t readEncodedPointer(const uint8_t** data, uint8_t encoding, + uintptr_t base = 0) { + uintptr_t result = 0; + if (encoding == DW_EH_PE_omit) return result; + const uint8_t* p = *data; + // first get value + switch (encoding & 0x0F) { + case DW_EH_PE_absptr: + result = readPointerHelper(p); + break; + case DW_EH_PE_uleb128: + result = readULEB128(&p); + break; + case DW_EH_PE_sleb128: + result = static_cast(readSLEB128(&p)); + break; + case DW_EH_PE_udata2: + result = readPointerHelper(p); + break; + case DW_EH_PE_udata4: + result = readPointerHelper(p); + break; + case DW_EH_PE_udata8: + result = readPointerHelper(p); + break; + case DW_EH_PE_sdata2: + result = readPointerHelper(p); + break; + case DW_EH_PE_sdata4: + result = readPointerHelper(p); + break; + case DW_EH_PE_sdata8: + result = readPointerHelper(p); + break; + default: + // not supported + abort(); + break; + } + // then add relative offset + switch (encoding & 0x70) { + case DW_EH_PE_absptr: + // do nothing + break; + case DW_EH_PE_pcrel: + if (result) + result += (uintptr_t)(*data); + break; + case DW_EH_PE_datarel: + assert((base != 0) && "DW_EH_PE_datarel is invalid with a base of 0"); + if (result) + result += base; + break; + case DW_EH_PE_textrel: + case DW_EH_PE_funcrel: + case DW_EH_PE_aligned: + default: + // not supported + abort(); + break; + } + // then apply indirection + if (result && (encoding & DW_EH_PE_indirect)) + result = *((uintptr_t*)result); + *data = p; + return result; } static @@ -348,59 +349,55 @@ static const __shim_type_info* get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo, uint8_t ttypeEncoding, bool native_exception, - _Unwind_Exception* unwind_exception) -{ - if (classInfo == 0) - { - // this should not happen. Indicates corrupted eh_table. - call_terminate(native_exception, unwind_exception); - } - - assert(((ttypeEncoding == DW_EH_PE_absptr) || // LLVM or GCC 4.6 - (ttypeEncoding == DW_EH_PE_pcrel) || // GCC 4.7 baremetal - (ttypeEncoding == (DW_EH_PE_pcrel | DW_EH_PE_indirect))) && // GCC 4.7 linux - "Unexpected TTypeEncoding"); - (void)ttypeEncoding; - - const uint8_t* ttypePtr = classInfo - ttypeIndex * sizeof(uintptr_t); - return reinterpret_cast( - read_target2_value(ttypePtr)); + _Unwind_Exception* unwind_exception, uintptr_t base = 0) { + if (classInfo == 0) { + // this should not happen. Indicates corrupted eh_table. + call_terminate(native_exception, unwind_exception); + } + + assert(((ttypeEncoding == DW_EH_PE_absptr) || // LLVM or GCC 4.6 + (ttypeEncoding == DW_EH_PE_pcrel) || // GCC 4.7 baremetal + (ttypeEncoding == + (DW_EH_PE_pcrel | DW_EH_PE_indirect))) && // GCC 4.7 linux + "Unexpected TTypeEncoding"); + (void)ttypeEncoding; + + const uint8_t* ttypePtr = classInfo - ttypeIndex * sizeof(uintptr_t); + return reinterpret_cast( + read_target2_value(ttypePtr)); } #else // !defined(_LIBCXXABI_ARM_EHABI) -static -const __shim_type_info* +static const __shim_type_info* get_shim_type_info(uint64_t ttypeIndex, const uint8_t* classInfo, uint8_t ttypeEncoding, bool native_exception, - _Unwind_Exception* unwind_exception) -{ - if (classInfo == 0) - { - // this should not happen. Indicates corrupted eh_table. - call_terminate(native_exception, unwind_exception); - } - switch (ttypeEncoding & 0x0F) - { - case DW_EH_PE_absptr: - ttypeIndex *= sizeof(void*); - break; - case DW_EH_PE_udata2: - case DW_EH_PE_sdata2: - ttypeIndex *= 2; - break; - case DW_EH_PE_udata4: - case DW_EH_PE_sdata4: - ttypeIndex *= 4; - break; - case DW_EH_PE_udata8: - case DW_EH_PE_sdata8: - ttypeIndex *= 8; - break; - default: - // this should not happen. Indicates corrupted eh_table. - call_terminate(native_exception, unwind_exception); - } - classInfo -= ttypeIndex; - return (const __shim_type_info*)readEncodedPointer(&classInfo, ttypeEncoding); + _Unwind_Exception* unwind_exception, uintptr_t base = 0) { + if (classInfo == 0) { + // this should not happen. Indicates corrupted eh_table. + call_terminate(native_exception, unwind_exception); + } + switch (ttypeEncoding & 0x0F) { + case DW_EH_PE_absptr: + ttypeIndex *= sizeof(void*); + break; + case DW_EH_PE_udata2: + case DW_EH_PE_sdata2: + ttypeIndex *= 2; + break; + case DW_EH_PE_udata4: + case DW_EH_PE_sdata4: + ttypeIndex *= 4; + break; + case DW_EH_PE_udata8: + case DW_EH_PE_sdata8: + ttypeIndex *= 8; + break; + default: + // this should not happen. Indicates corrupted eh_table. + call_terminate(native_exception, unwind_exception); + } + classInfo -= ttypeIndex; + return (const __shim_type_info*)readEncodedPointer(&classInfo, ttypeEncoding, + base); } #endif // !defined(_LIBCXXABI_ARM_EHABI) @@ -414,83 +411,73 @@ excpType, then this exception spec does not catch the excpType. */ #if defined(_LIBCXXABI_ARM_EHABI) -static -bool -exception_spec_can_catch(int64_t specIndex, const uint8_t* classInfo, - uint8_t ttypeEncoding, const __shim_type_info* excpType, - void* adjustedPtr, _Unwind_Exception* unwind_exception) -{ - if (classInfo == 0) - { - // this should not happen. Indicates corrupted eh_table. - call_terminate(false, unwind_exception); - } - - assert(((ttypeEncoding == DW_EH_PE_absptr) || // LLVM or GCC 4.6 - (ttypeEncoding == DW_EH_PE_pcrel) || // GCC 4.7 baremetal - (ttypeEncoding == (DW_EH_PE_pcrel | DW_EH_PE_indirect))) && // GCC 4.7 linux - "Unexpected TTypeEncoding"); - (void)ttypeEncoding; - - // specIndex is negative of 1-based byte offset into classInfo; - specIndex = -specIndex; - --specIndex; - const void** temp = reinterpret_cast( - reinterpret_cast(classInfo) + - static_cast(specIndex) * sizeof(uintptr_t)); - // If any type in the spec list can catch excpType, return false, else return true - // adjustments to adjustedPtr are ignored. - while (true) - { - // ARM EHABI exception specification table (filter table) consists of - // several pointers which will directly point to the type info object - // (instead of ttypeIndex). The table will be terminated with 0. - const void** ttypePtr = temp++; - if (*ttypePtr == 0) - break; - // We can get the __shim_type_info simply by performing a - // R_ARM_TARGET2 relocation, and cast the result to __shim_type_info. - const __shim_type_info* catchType = - static_cast(read_target2_value(ttypePtr)); - void* tempPtr = adjustedPtr; - if (catchType->can_catch(excpType, tempPtr)) - return false; - } - return true; +static bool exception_spec_can_catch( + int64_t specIndex, const uint8_t* classInfo, uint8_t ttypeEncoding, + const __shim_type_info* excpType, void* adjustedPtr, + _Unwind_Exception* unwind_exception, uintptr_t base = 0) { + if (classInfo == 0) { + // this should not happen. Indicates corrupted eh_table. + call_terminate(false, unwind_exception); + } + + assert(((ttypeEncoding == DW_EH_PE_absptr) || // LLVM or GCC 4.6 + (ttypeEncoding == DW_EH_PE_pcrel) || // GCC 4.7 baremetal + (ttypeEncoding == + (DW_EH_PE_pcrel | DW_EH_PE_indirect))) && // GCC 4.7 linux + "Unexpected TTypeEncoding"); + (void)ttypeEncoding; + + // specIndex is negative of 1-based byte offset into classInfo; + specIndex = -specIndex; + --specIndex; + const void** temp = reinterpret_cast( + reinterpret_cast(classInfo) + + static_cast(specIndex) * sizeof(uintptr_t)); + // If any type in the spec list can catch excpType, return false, else return true + // adjustments to adjustedPtr are ignored. + while (true) { + // ARM EHABI exception specification table (filter table) consists of + // several pointers which will directly point to the type info object + // (instead of ttypeIndex). The table will be terminated with 0. + const void** ttypePtr = temp++; + if (*ttypePtr == 0) + break; + // We can get the __shim_type_info simply by performing a + // R_ARM_TARGET2 relocation, and cast the result to __shim_type_info. + const __shim_type_info* catchType = + static_cast(read_target2_value(ttypePtr)); + void* tempPtr = adjustedPtr; + if (catchType->can_catch(excpType, tempPtr)) + return false; + } + return true; } #else -static -bool -exception_spec_can_catch(int64_t specIndex, const uint8_t* classInfo, - uint8_t ttypeEncoding, const __shim_type_info* excpType, - void* adjustedPtr, _Unwind_Exception* unwind_exception) -{ - if (classInfo == 0) - { - // this should not happen. Indicates corrupted eh_table. - call_terminate(false, unwind_exception); - } - // specIndex is negative of 1-based byte offset into classInfo; - specIndex = -specIndex; - --specIndex; - const uint8_t* temp = classInfo + specIndex; - // If any type in the spec list can catch excpType, return false, else return true - // adjustments to adjustedPtr are ignored. - while (true) - { - uint64_t ttypeIndex = readULEB128(&temp); - if (ttypeIndex == 0) - break; - const __shim_type_info* catchType = get_shim_type_info(ttypeIndex, - classInfo, - ttypeEncoding, - true, - unwind_exception); - void* tempPtr = adjustedPtr; - if (catchType->can_catch(excpType, tempPtr)) - return false; - } - return true; +static bool exception_spec_can_catch( + int64_t specIndex, const uint8_t* classInfo, uint8_t ttypeEncoding, + const __shim_type_info* excpType, void* adjustedPtr, + _Unwind_Exception* unwind_exception, uintptr_t base = 0) { + if (classInfo == 0) { + // this should not happen. Indicates corrupted eh_table. + call_terminate(false, unwind_exception); + } + // specIndex is negative of 1-based byte offset into classInfo; + specIndex = -specIndex; + --specIndex; + const uint8_t* temp = classInfo + specIndex; + // If any type in the spec list can catch excpType, return false, else return true + // adjustments to adjustedPtr are ignored. + while (true) { + uint64_t ttypeIndex = readULEB128(&temp); + if (ttypeIndex == 0) + break; + const __shim_type_info* catchType = get_shim_type_info( + ttypeIndex, classInfo, ttypeEncoding, true, unwind_exception, base); + void* tempPtr = adjustedPtr; + if (catchType->can_catch(excpType, tempPtr)) + return false; + } + return true; } #endif @@ -531,6 +518,9 @@ { #if defined(__USING_SJLJ_EXCEPTIONS__) #define __builtin_eh_return_data_regno(regno) regno +#elif defined(__ibmxl__) +// IBM xlclang++ compiler does not support __builtin_eh_return_data_regno. +#define __builtin_eh_return_data_regno(regno) regno + 3 #endif _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), reinterpret_cast(unwind_exception)); @@ -610,6 +600,11 @@ return; } results.languageSpecificData = lsda; +#if defined(_AIX) + uintptr_t base = _Unwind_GetDataRelBase(context); +#else + uintptr_t base = 0; +#endif // Get the current instruction pointer and offset it before next // instruction in the current frame which threw the exception. uintptr_t ip = _Unwind_GetIP(context) - 1; @@ -708,10 +703,10 @@ { // Found a catch, does it actually catch? // First check for catch (...) - const __shim_type_info* catchType = - get_shim_type_info(static_cast(ttypeIndex), - classInfo, ttypeEncoding, - native_exception, unwind_exception); + const __shim_type_info* catchType = get_shim_type_info( + static_cast(ttypeIndex), classInfo, + ttypeEncoding, native_exception, unwind_exception, + base); if (catchType == 0) { // Found catch (...) catches everything, including @@ -770,18 +765,17 @@ // Something very bad happened call_terminate(native_exception, unwind_exception); } - if (exception_spec_can_catch(ttypeIndex, classInfo, - ttypeEncoding, excpType, - adjustedPtr, unwind_exception)) - { - // Native exception caught by exception - // specification. - assert(actions & _UA_SEARCH_PHASE); - results.ttypeIndex = ttypeIndex; - results.actionRecord = actionRecord; - results.adjustedPtr = adjustedPtr; - results.reason = _URC_HANDLER_FOUND; - return; + if (exception_spec_can_catch( + ttypeIndex, classInfo, ttypeEncoding, excpType, + adjustedPtr, unwind_exception, base)) { + // Native exception caught by exception + // specification. + assert(actions & _UA_SEARCH_PHASE); + results.ttypeIndex = ttypeIndex; + results.actionRecord = actionRecord; + results.adjustedPtr = adjustedPtr; + results.reason = _URC_HANDLER_FOUND; + return; } } else { // foreign exception caught by exception spec @@ -911,6 +905,12 @@ // Jump to the handler. set_registers(unwind_exception, context, results); +#if defined(_AIX) + // Cache ttype base for __cxa_call_unexpected. + if (results.ttypeIndex < 0) { + exception_header->catchTemp = (void*)_Unwind_GetDataRelBase(context); + } +#endif return _URC_INSTALL_CONTEXT; } @@ -940,6 +940,14 @@ assert(actions & _UA_CLEANUP_PHASE); assert(results.reason == _URC_HANDLER_FOUND); set_registers(unwind_exception, context, results); +#if defined(_AIX) + // Cache ttype base for __cxa_call_unexpected. + if (results.ttypeIndex < 0) { + __cxa_exception* exception_header = + (__cxa_exception*)(unwind_exception + 1) - 1; + exception_header->catchTemp = (void*)_Unwind_GetDataRelBase(context); + } +#endif return _URC_INSTALL_CONTEXT; } @@ -1114,6 +1122,8 @@ __cxa_exception* old_exception_header = 0; int64_t ttypeIndex; const uint8_t* lsda; + uintptr_t base = 0; + if (native_old_exception) { old_exception_header = (__cxa_exception*)(unwind_exception+1) - 1; @@ -1127,6 +1137,7 @@ #else ttypeIndex = old_exception_header->handlerSwitchValue; lsda = old_exception_header->languageSpecificData; + base = (uintptr_t)old_exception_header->catchTemp; #endif } else @@ -1147,60 +1158,67 @@ // from here. if (native_old_exception) { - // Have: - // old_exception_header->languageSpecificData - // old_exception_header->actionRecord - // Need - // const uint8_t* classInfo - // uint8_t ttypeEncoding - uint8_t lpStartEncoding = *lsda++; - const uint8_t* lpStart = (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding); - (void)lpStart; // purposefully unused. Just needed to increment lsda. - uint8_t ttypeEncoding = *lsda++; - if (ttypeEncoding == DW_EH_PE_omit) - std::__terminate(t_handler); - uintptr_t classInfoOffset = readULEB128(&lsda); - const uint8_t* classInfo = lsda + classInfoOffset; - // Is this new exception catchable by the exception spec at ttypeIndex? - // The answer is obviously yes if the new and old exceptions are the same exception - // If no - // throw; - __cxa_eh_globals* globals = __cxa_get_globals_fast(); - __cxa_exception* new_exception_header = globals->caughtExceptions; - if (new_exception_header == 0) - // This shouldn't be able to happen! - std::__terminate(t_handler); - bool native_new_exception = __isOurExceptionClass(&new_exception_header->unwindHeader); - void* adjustedPtr; - if (native_new_exception && (new_exception_header != old_exception_header)) - { - const __shim_type_info* excpType = - static_cast(new_exception_header->exceptionType); - adjustedPtr = - __getExceptionClass(&new_exception_header->unwindHeader) == kOurDependentExceptionClass ? - ((__cxa_dependent_exception*)new_exception_header)->primaryException : - new_exception_header + 1; - if (!exception_spec_can_catch(ttypeIndex, classInfo, ttypeEncoding, - excpType, adjustedPtr, unwind_exception)) - { - // We need to __cxa_end_catch, but for the old exception, - // not the new one. This is a little tricky ... - // Disguise new_exception_header as a rethrown exception, but - // don't actually rethrow it. This means you can temporarily - // end the catch clause enclosing new_exception_header without - // __cxa_end_catch destroying new_exception_header. - new_exception_header->handlerCount = -new_exception_header->handlerCount; - globals->uncaughtExceptions += 1; - // Call __cxa_end_catch for new_exception_header - __cxa_end_catch(); - // Call __cxa_end_catch for old_exception_header - __cxa_end_catch(); - // Renter this catch clause with new_exception_header - __cxa_begin_catch(&new_exception_header->unwindHeader); - // Rethrow new_exception_header - throw; - } + // Have: + // old_exception_header->languageSpecificData + // old_exception_header->actionRecord + // old_exception_header->catchTemp, i.e., ttype_base + // Need + // const uint8_t* classInfo + // uint8_t ttypeEncoding + uint8_t lpStartEncoding = *lsda++; + const uint8_t* lpStart = + (const uint8_t*)readEncodedPointer(&lsda, lpStartEncoding); + (void)lpStart; // purposefully unused. Just needed to increment lsda. + uint8_t ttypeEncoding = *lsda++; + if (ttypeEncoding == DW_EH_PE_omit) + std::__terminate(t_handler); + uintptr_t classInfoOffset = readULEB128(&lsda); + const uint8_t* classInfo = lsda + classInfoOffset; + // Is this new exception catchable by the exception spec at ttypeIndex? + // The answer is obviously yes if the new and old exceptions are the same exception + // If no + // throw; + __cxa_eh_globals* globals = __cxa_get_globals_fast(); + __cxa_exception* new_exception_header = globals->caughtExceptions; + if (new_exception_header == 0) + // This shouldn't be able to happen! + std::__terminate(t_handler); + bool native_new_exception = + __isOurExceptionClass(&new_exception_header->unwindHeader); + void* adjustedPtr; + if (native_new_exception && + (new_exception_header != old_exception_header)) { + const __shim_type_info* excpType = + static_cast( + new_exception_header->exceptionType); + adjustedPtr = + __getExceptionClass(&new_exception_header->unwindHeader) == + kOurDependentExceptionClass + ? ((__cxa_dependent_exception*)new_exception_header) + ->primaryException + : new_exception_header + 1; + if (!exception_spec_can_catch(ttypeIndex, classInfo, ttypeEncoding, + excpType, adjustedPtr, + unwind_exception, base)) { + // We need to __cxa_end_catch, but for the old exception, + // not the new one. This is a little tricky ... + // Disguise new_exception_header as a rethrown exception, but + // don't actually rethrow it. This means you can temporarily + // end the catch clause enclosing new_exception_header without + // __cxa_end_catch destroying new_exception_header. + new_exception_header->handlerCount = + -new_exception_header->handlerCount; + globals->uncaughtExceptions += 1; + // Call __cxa_end_catch for new_exception_header + __cxa_end_catch(); + // Call __cxa_end_catch for old_exception_header + __cxa_end_catch(); + // Renter this catch clause with new_exception_header + __cxa_begin_catch(&new_exception_header->unwindHeader); + // Rethrow new_exception_header + throw; } + } // Will a std::bad_exception be catchable by the exception spec at // ttypeIndex? // If no @@ -1210,22 +1228,576 @@ std::bad_exception be; adjustedPtr = &be; if (!exception_spec_can_catch(ttypeIndex, classInfo, ttypeEncoding, - excpType, adjustedPtr, unwind_exception)) - { - // We need to __cxa_end_catch for both the old exception and the - // new exception. Technically we should do it in that order. - // But it is expedient to do it in the opposite order: - // Call __cxa_end_catch for new_exception_header - __cxa_end_catch(); - // Throw std::bad_exception will __cxa_end_catch for - // old_exception_header - throw be; + excpType, adjustedPtr, + unwind_exception, base)) { + // We need to __cxa_end_catch for both the old exception and the + // new exception. Technically we should do it in that order. + // But it is expedient to do it in the opposite order: + // Call __cxa_end_catch for new_exception_header + __cxa_end_catch(); + // Throw std::bad_exception will __cxa_end_catch for + // old_exception_header + throw be; } } } std::__terminate(t_handler); } +#if defined(_AIX) +// Macros for debugging state table parsing. +#ifdef NDEBUG +#define _LIBCXXABI_TRACE_STATETAB(_args...) +#define _LIBCXXABI_TRACE_STATETAB0(_args...) +#define _LIBCXXABI_TRACING_STATETAB (0) +#else +bool StateTabDbg() { + static bool checked = false; + static bool log = false; + if (!checked) { + log = (getenv("LIBCXXABI_PRINT_STATTAB") != NULL); + checked = true; + } + return log; +} + +#define _LIBCXXABI_TRACE_STATETAB(_args...) \ + do { \ + if (StateTabDbg()) \ + fprintf(stderr, "libcxxabi: " _args); \ + } while (0) +#define _LIBCXXABI_TRACE_STATETAB0(_args...) \ + do { \ + if (StateTabDbg()) \ + fprintf(stderr, _args); \ + } while (0) +#define _LIBCXXABI_TRACING_STATETAB StateTabDbg() +#endif + +typedef void (*destruct_f)(void*); + +// The finite state machine to be walked. +struct FSM_Entry { + long offset; + union { + void (*destructor)(void*, size_t); + void* catch_block; + }; + long count; + size_t elem_size; + unsigned short flags; + unsigned short next_state; +}; + +struct FSM { + unsigned int magic; + int number_of_states; + FSM_Entry table[1]; // Actually table[number_of_states] +}; + +// The state variable on the stack +struct StateVariable { + int state; + struct FSM* table; + long this_value; + int ignore_vbase_ptrs; +}; + +#define FSM_MAGIC 0xbeefdead +#define FSM_MAGIC2 0xbeeedead +#define FSM_MAGIC3 0x1cedbeef + +#define BEGIN_CATCH_COUNT -1 +#define END_CATCH_COUNT -2 +#define DELETE_OBJECT_COUNT -3 +#define INLINE_DESTRUCTOR_COUNT -4 +#define TERMINATE_COUNT -5 + +#define STATE_CHANGE_OLD \ + 0x400 // State table entry is an indirect state \ + // change, dereference the address in \ + // offset as int for the target state. \ + // This is deplicated. This indicate \ + // the address is direct. (static local) +#define STATE_CHANGE \ + 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. + +#define THIS_FLAG 0x01 +#define VBASE_FLAG 0x02 +#define GLOBAL_OBJ 0x04 + +#define INDIRECT \ + 0x100 // Object was thrown from a function \ + // where the return value optimization \ + // was used. +#define DTR_ARGUMENT 0x2 // Free virtual bases, don't delete object + +static void __Invoke__Destructor(FSM_Entry* fsmEntry, void* addr) { + _LIBCXXABI_TRACE_STATETAB("Deallocate object=%p, fsmEntry=%p\n", addr, + (void*)fsmEntry); + try { + if (fsmEntry->count == 1) { + _LIBCXXABI_TRACE_STATETAB("calling scaler destructor\n"); + (*fsmEntry->destructor)(addr, DTR_ARGUMENT); + _LIBCXXABI_TRACE_STATETAB("returned from scaler destructor\n"); + } else { + _LIBCXXABI_TRACE_STATETAB("calling vector destructor\n"); + __cxa_vec_cleanup(addr, (size_t)fsmEntry->count, fsmEntry->elem_size, + (destruct_f)fsmEntry->destructor); + _LIBCXXABI_TRACE_STATETAB("returned from vector destructor\n"); + } + } catch (...) { + _LIBCXXABI_TRACE_STATETAB("Uncaught exception in destructor, " + "terminating\n"); + std::terminate(); + } +} + +static void __Invoke__Delete(FSM_Entry* fsmEntry, void* addr) { + char* objectAddress = *(char**)addr; + + _LIBCXXABI_TRACE_STATETAB("Delete object=%p, fsmEntry=%p\n", + (void*)objectAddress, (void*)fsmEntry); + try { + _LIBCXXABI_TRACE_STATETAB("..calling delete()\n"); + // 'destructor' holds a function pointer to delete(). + (*fsmEntry->destructor)(objectAddress, fsmEntry->elem_size); + _LIBCXXABI_TRACE_STATETAB("..returned from delete()\n"); + } catch (...) { + _LIBCXXABI_TRACE_STATETAB("Uncaught exception in delete(), " + "terminating\n"); + std::terminate(); + } +} + +// __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, return false. +extern "C" bool __xlc_catch_matchv2(_Unwind_Exception* exceptionObject, + std::type_info* catch_ti, void*& obj) { + _LIBCXXABI_TRACE_STATETAB("Entering %s, exceptionObject=%p\n", __func__, + (void*)exceptionObject); + + if (!__isOurExceptionClass(exceptionObject)) { + // If it is not a c++ exception, return 0 to indicate no match + _LIBCXXABI_TRACE_STATETAB("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 = + (__cxa_dependent_exception*)(exceptionObject + 1) - 1; + exceptionHeader = static_cast<__cxa_exception*>( + dependentExceptionHeader->primaryException) - + 1; + _LIBCXXABI_TRACE_STATETAB("exceptionObject 0x%p is a dependent, " + "primary 0x%p\n", + (void*)exceptionObject, + (void*)&exceptionHeader->unwindHeader); + exceptionObject = &exceptionHeader->unwindHeader; + } else { + _LIBCXXABI_TRACE_STATETAB("exceptionObject %p is NOT a dependent\n", + (void*)exceptionObject); + exceptionHeader = ((__cxa_exception*)(exceptionObject + 1) - 1); + } + + void* thrownObject = (void*)(exceptionObject + 1); + std::type_info* throw_ti = 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 = + static_cast<__cxxabiv1::__shim_type_info*>(catch_ti); + __cxxabiv1::__shim_type_info* throwType = + static_cast<__cxxabiv1::__shim_type_info*>(throw_ti); + _LIBCXXABI_TRACE_STATETAB("UnwindException=%p thrownObject=%p " + "t-ti=%p(%s) c-ti=%p(%s)\n", + (void*)exceptionObject, thrownObject, + (void*)throwType, throwType->name(), + (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_STATETAB("No match\n"); + return false; +} + +const uint32_t gRegisterNum = 14; // The GPR to pass the exception object + // address for xlclang++ compiled code. + +// Calculate the object address from the FSM entry +static void* compute_obj_addr(FSM_Entry* fsmEntry, StateVariable* const state) { + void* addr; + if (fsmEntry->flags & GLOBAL_OBJ) { + addr = (void*)fsmEntry->offset; + _LIBCXXABI_TRACE_STATETAB("Address calculation (global obj) " + "addr=currEntry->offset=%p\n", + addr); + } else if (fsmEntry->flags & THIS_FLAG) { + addr = (void*)(fsmEntry->offset + state->this_value); + _LIBCXXABI_TRACE_STATETAB("Address calculation (this obj) " + "currEntry->offset=%ld : " + "state->this_value=%ld addr=" + "(currEntry-->offset+state->this_value)=%p\n", + fsmEntry->offset, state->this_value, addr); + } else if (fsmEntry->flags & INDIRECT) { + addr = (void*)*(char**)(fsmEntry->offset); + _LIBCXXABI_TRACE_STATETAB("Address calculation (indirect obj) " + "addr=%p, fsmEntry->offset=%ld \n", + addr, fsmEntry->offset); + } else { + addr = (void*)(fsmEntry->offset); + _LIBCXXABI_TRACE_STATETAB("Address calculation. (local obj) " + "addr=currEntry->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_STATETAB0("\n"); + _LIBCXXABI_TRACE_STATETAB("%s: actions=%d (", __func__, actions); + + if (_UA_SEARCH_PHASE & actions) + _LIBCXXABI_TRACE_STATETAB0("_UA_SEARCH_PHASE "); + if (_UA_CLEANUP_PHASE & actions) + _LIBCXXABI_TRACE_STATETAB0("_UA_CLEANUP_PHASE "); + if (_UA_HANDLER_FRAME & actions) + _LIBCXXABI_TRACE_STATETAB0("_UA_HANDLER_FRAME "); + if (_UA_FORCE_UNWIND & actions) + _LIBCXXABI_TRACE_STATETAB0("_UA_FORCE_UNWIND "); + _LIBCXXABI_TRACE_STATETAB0(")\n"); + _LIBCXXABI_TRACE_STATETAB(" unwind_exception=%p context=%p\n", + (void*)unwind_exception, (void*)context); + } + + // Start scan by getting state table address + StateVariable* const state = + (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; + + _LIBCXXABI_TRACE_STATETAB("state->state=%d, state->ignore_vbase_ptrs=%d\n", + state->state, state->ignore_vbase_ptrs); + + // The magic number is FSM_MAGIC3 for xlclang++. + _LIBCXXABI_TRACE_STATETAB("fsm->magic=%#x, fsm->number_of_states=%d\n", + fsm->magic, fsm->number_of_states); + + if (fsm->magic == FSM_MAGIC || fsm->magic == FSM_MAGIC2 || + fsm->magic == FSM_MAGIC3) { + // Debug display of the FSM table. + if (_LIBCXXABI_TRACING_STATETAB) { + // Print out the FSM table. + _LIBCXXABI_TRACE_STATETAB("FSM table:\n"); + _LIBCXXABI_TRACE_STATETAB("%12s %10s %8s %10s %7s %7s %7s %7s\n", + "Entry Addr", "state", "Offset", "DTR addr", + "count", "el_size", "flags", "next"); + for (int i = 0; i < fsm->number_of_states; i++) { + FSM_Entry& f = fsm->table[i]; + _LIBCXXABI_TRACE_STATETAB("%12p (%8d) %8ld %10p %7ld " + "%7ld %#7x %7d\n", + (void*)&f, i + 1, f.offset, + (void*)f.destructor, f.count, f.elem_size, + f.flags, f.next_state); + } + } + } else { + // Something is wrong the the state table we found. + if (_UA_SEARCH_PHASE & actions) { + _LIBCXXABI_TRACE_STATETAB("Invalid FSM table, " + "return _URC_FATAL_PHASE1_ERROR\n"); + results.reason = _URC_FATAL_PHASE1_ERROR; + return; + } else if (_UA_CLEANUP_PHASE & actions) { + _LIBCXXABI_TRACE_STATETAB("Invalid FSM table, " + "return _URC_FATAL_PHASE2_ERROR\n"); + results.reason = _URC_FATAL_PHASE2_ERROR; + return; + } else { + // We should never get here. + _LIBCXXABI_TRACE_STATETAB("Invalid FSM table + RT Internal error," + " return _URC_FATAL_PHASE2_ERROR\n"); + results.reason = _URC_FATAL_PHASE2_ERROR; + return; + } + } + + if (_UA_SEARCH_PHASE & actions) { + // Start walking the 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) { + FSM_Entry* currEntry = &fsm->table[currState - 1]; + _LIBCXXABI_TRACE_STATETAB("Processing state=%d, flags=0x%hx\n", currState, + currEntry->flags); + + void* addr = compute_obj_addr(currEntry, state); + + if (currEntry->count == BEGIN_CATCH_COUNT) { + // Found a catch handler. + _LIBCXXABI_TRACE_STATETAB("Found a catch handler, " + "return _URC_HANDLER_FOUND\n"); + results.reason = _URC_HANDLER_FOUND; + return; + } else if (currEntry->count == TERMINATE_COUNT) { + _LIBCXXABI_TRACE_STATETAB("Found the terminate state, " + "return _URC_HANDLER_FOUND\n"); + results.reason = _URC_HANDLER_FOUND; + return; + } else if (currEntry->flags & STATE_CHANGE_OLD) { + // Deplicated conditional expression. + currState = *((int*)(currEntry->offset)); + _LIBCXXABI_TRACE_STATETAB("Flag: STATE_CHANGE_OLD, dereference " + "currEntry->offset(%ld), set state=%d\n", + currEntry->offset, currState); + continue; // We are done this iteration of the loop, since + // we changed a state. + } else if (currEntry->flags & STATE_CHANGE) { + state->state = *((int*)(addr)); + _LIBCXXABI_TRACE_STATETAB("Flag: STATE_CHANGE, dereference " + "addr(%p), set state=%d\n", + addr, state->state); + continue; // We are done this iteration of the loop, since we + // changed the state. + } + + // Go to the next state. + currState = currEntry->next_state; + } + _LIBCXXABI_TRACE_STATETAB("No catch handler found, " + "return _URC_CONTINUE_UNWIND\n"); + results.reason = _URC_CONTINUE_UNWIND; + return; + } else if (_UA_CLEANUP_PHASE & actions) { + // Start walking the table. + while (state->state > 0) { + FSM_Entry* currEntry = &fsm->table[state->state - 1]; + + if (currEntry->count == TERMINATE_COUNT) { + _LIBCXXABI_TRACE_STATETAB("Reached terminate state. " + "Call terminate.\n"); + std::terminate(); + } + + void* addr = compute_obj_addr(currEntry, state); + + // Perform action according to the currEntry->count, + // except when flag is STATE_CHANGE or STATE_CHANGE_OLD. + _LIBCXXABI_TRACE_STATETAB("Processing state=%d, flags=0x%hx\n", + state->state, currEntry->flags); + if (currEntry->flags & STATE_CHANGE_OLD) { + state->state = *((int*)(currEntry->offset)); + _LIBCXXABI_TRACE_STATETAB("Flag: STATE_CHANGE_OLD, dereference " + "currEntry->offset(%ld), set state=%d\n", + currEntry->offset, state->state); + continue; // We are done with this iteration of the loop, since + // we changed a state. + } else if (currEntry->flags & STATE_CHANGE) { + state->state = *((int*)(addr)); + _LIBCXXABI_TRACE_STATETAB("Flag: STATE_CHANGE, 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. + } else if (currEntry->count > 0) { + if (currEntry->flags & VBASE_FLAG && state->ignore_vbase_ptrs) { + _LIBCXXABI_TRACE_STATETAB("Ignoring virtual base dtor.\n"); + } else { + _LIBCXXABI_TRACE_STATETAB("Invoke dtor for object=%p\n", addr); + __Invoke__Destructor(currEntry, addr); + } + } else if (currEntry->count == DELETE_OBJECT_COUNT) { + _LIBCXXABI_TRACE_STATETAB("Delete object at %p\n", addr); + __Invoke__Delete(currEntry, addr); + } else if (currEntry->count == BEGIN_CATCH_COUNT) { + // Branch to the landing pad to see if the exception can be + // caught. + _LIBCXXABI_TRACE_STATETAB("BEGIN_CATCH_COUNT: handler " + "%p/%p, return _URC_HANDLER_FOUND\n", + currEntry->catch_block, + *(void**)(currEntry->catch_block)); + _LIBCXXABI_TRACE_STATETAB("Changed to state=%d\n", state->state); + state->state = currEntry->next_state; + results.landingPad = (uintptr_t) * (void**)(currEntry->catch_block); + results.reason = _URC_HANDLER_FOUND; + return; + } else if (currEntry->count == END_CATCH_COUNT) { + // End current catch block and branch to the landing pad. + _LIBCXXABI_TRACE_STATETAB("END_CATCH_COUNT: handler " + "%p/%p, return _URC_HANDLER_FOUND\n", + currEntry->catch_block, + *(void**)(currEntry->catch_block)); + _LIBCXXABI_TRACE_STATETAB("Changed to state=%d\n", state->state); + state->state = currEntry->next_state; + results.landingPad = (uintptr_t) * (void**)(currEntry->catch_block); + results.reason = _URC_HANDLER_FOUND; + return; + } else if (currEntry->count == INLINE_DESTRUCTOR_COUNT) { + // Branch to user code to perform the action and eventually + // the inline unwinding will call _Unwind_resume. + _LIBCXXABI_TRACE_STATETAB("INLINE_DESTRUCTOR_COUNT: handler " + "%p/%p, return _URC_HANDLER_FOUND\n", + currEntry->catch_block, + *(void**)(currEntry->catch_block)); + _LIBCXXABI_TRACE_STATETAB("Changed to state=%d\n", state->state); + state->state = currEntry->next_state; + results.landingPad = (uintptr_t) * (void**)(currEntry->catch_block); + results.reason = _URC_HANDLER_FOUND; + return; + } else { + _LIBCXXABI_TRACE_STATETAB("Unknown entry in FSM (count=%ld), " + "ignored\n", + currEntry->count); + } // End of action switching + + // Go to next state. + state->state = currEntry->next_state; + } + _LIBCXXABI_TRACE_STATETAB("No catch handler, " + "return _URC_CONTINUE_UNWIND\n"); + results.reason = _URC_CONTINUE_UNWIND; + return; + } + _LIBCXXABI_TRACE_STATETAB("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 state table. +_Unwind_Reason_Code __xlcxx_personality_v0(int version, _Unwind_Action actions, + uint64_t exceptionClass, + _Unwind_Exception* unwind_exception, + _Unwind_Context* context) { + if (version != 1 || unwind_exception == 0 || context == 0) + return _URC_FATAL_PHASE1_ERROR; + + bool native_exception = (exceptionClass & get_vendor_and_language) == + (kOurExceptionClass & get_vendor_and_language); + scan_results results; + scan_state_tab(results, actions, native_exception, unwind_exception, context); + if (actions & _UA_SEARCH_PHASE) { + // Phase 1 search: All we're looking for in phase 1 is a handler that + // halts unwinding + return results.reason; + } + if (actions & _UA_CLEANUP_PHASE) { + // Phase 2 cleanup: + if (results.reason == _URC_HANDLER_FOUND) { + // Jump to the handler. + _Unwind_SetGR(context, gRegisterNum, + reinterpret_cast(unwind_exception)); + _Unwind_SetIP(context, results.landingPad); + return _URC_INSTALL_CONTEXT; + } + // Did not find a handler. Return the results of the scan. Normally + // _URC_CONTINUE_UNWIND, but could have been _URC_FATAL_PHASE2_ERROR. + return results.reason; + } + // We were called improperly: neither a phase 1 or phase 2 search. + return _URC_FATAL_PHASE1_ERROR; +} + +// Personality routine for EH using the range table. Make it an alias of +// __gxx_personality_v0(). +_LIBCXXABI_FUNC_VIS _Unwind_Reason_Code __xlcxx_personality_v1( + int version, _Unwind_Action actions, uint64_t exceptionClass, + _Unwind_Exception* unwind_exception, _Unwind_Context* context) + __attribute__((alias("__gxx_personality_v0"))); + +// __xlc_throw_badexception +// This function is for xlclang++. It allocates and throws a badexception. +// During unwinding for this badexception, 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 badexception. +extern "C" void __xlc_throw_badexception() { + _LIBCXXABI_TRACE_STATETAB("Entering function: %s\n\n", __func__); + void* newexception = __cxa_allocate_exception(sizeof(std::bad_exception)); + __cxa_throw(newexception, (std::type_info*)&typeid(std::bad_exception), 0); +} + +// __xlc_exception_handle +// This function is for xlclang++. It returns the address of the exception +// object set in gpr14 by the personality routine for xlclang++ compiled code. +uintptr_t __xlc_exception_handle() { + //register unsigned long exceptionObject asm("r14"); + uintptr_t exceptionObject; + + asm("mr %0, 14" : "=r"(exceptionObject)); + return exceptionObject; +} + +// This function is for xlclang++. +void __UnsupportedConditionalExpressionDestruction(void* addr, long flag) { + // We want to die instead of randomlly picking b or c to destory, which + // will be confusing for the user. + abort(); +} + +// This function is for xlclang++. It aborts when a deleted virtual +// function is called. +void __Deleted_Virtual() { abort(); } +#endif // defined(_AIX) + } // extern "C" } // __cxxabiv1