From d3dba31762233ae21f0388325055b24b64ce29aa Mon Sep 17 00:00:00 2001 From: Howard Hinnant Date: Fri, 6 Jan 2012 20:39:47 +0000 Subject: [PATCH] __gxx_personality_v0 update. This is completely untested code. But my brain is scrambled and I wanted to get it checked in. Code review from anyone who knows anything at all about personality functions would be much appreciated. What is in here is borrowed heavily from llvm/examples/ExceptionDemo/ExceptionDemo.cpp. There are some calls to abort() that should probably be changed to return an error code instead. There may be encodings under readEncodedPointer that need to be implemented. And my handling of type_info is almost a complete guess. llvm-svn: 147680 --- libcxxabi/src/cxa_personality.cpp | 291 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 283 insertions(+), 8 deletions(-) diff --git a/libcxxabi/src/cxa_personality.cpp b/libcxxabi/src/cxa_personality.cpp index 020f1d2..a241e93 100644 --- a/libcxxabi/src/cxa_personality.cpp +++ b/libcxxabi/src/cxa_personality.cpp @@ -8,10 +8,15 @@ // // This file implements the "Exception Handling APIs" // http://www.codesourcery.com/public/cxx-abi/abi-eh.html +// http://www.intel.com/design/itanium/downloads/245358.htm // //===----------------------------------------------------------------------===// #include "unwind.h" +#include "cxa_exception.hpp" +#include +#include +#include namespace __cxxabiv1 { @@ -21,6 +26,204 @@ extern "C" // private API +// Heavily borrowed from llvm/examples/ExceptionDemo/ExceptionDemo.cpp + +// DWARF Constants +enum +{ + DW_EH_PE_absptr = 0x00, + DW_EH_PE_uleb128 = 0x01, + DW_EH_PE_udata2 = 0x02, + DW_EH_PE_udata4 = 0x03, + DW_EH_PE_udata8 = 0x04, + DW_EH_PE_sleb128 = 0x09, + DW_EH_PE_sdata2 = 0x0A, + DW_EH_PE_sdata4 = 0x0B, + DW_EH_PE_sdata8 = 0x0C, + DW_EH_PE_pcrel = 0x10, + DW_EH_PE_textrel = 0x20, + DW_EH_PE_datarel = 0x30, + DW_EH_PE_funcrel = 0x40, + DW_EH_PE_aligned = 0x50, + DW_EH_PE_indirect = 0x80, + DW_EH_PE_omit = 0xFF +}; + +/// Read a uleb128 encoded value and advance pointer +/// See Variable Length Data Appendix C in: +/// @link http://dwarfstd.org/Dwarf4.pdf @unlink +/// @param data reference variable holding memory pointer to decode from +/// @returns decoded value +static +uintptr_t +readULEB128(const uint8_t** data) +{ + uintptr_t result = 0; + uintptr_t shift = 0; + unsigned char byte; + const uint8_t *p = *data; + do + { + byte = *p++; + result |= static_cast(byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + *data = p; + return result; +} + +/// Read a sleb128 encoded value and advance pointer +/// See Variable Length Data Applendix C in: +/// @link http://dwarfstd.org/Dwarf4.pdf @unlink +/// @param data reference variable holding memory pointer to decode from +/// @returns decoded value +static +uintptr_t +readSLEB128(const uint8_t** data) +{ + uintptr_t result = 0; + uintptr_t shift = 0; + unsigned char byte; + const uint8_t *p = *data; + do + { + byte = *p++; + result |= static_cast(byte & 0x7F) << shift; + shift += 7; + } while (byte & 0x80); + *data = p; + if ((byte & 0x40) && (shift < (sizeof(result) << 3))) + result |= static_cast(~0) << shift; + return result; +} + +/// Read a pointer encoded value and advance pointer +/// See Variable Length Data in: +/// @link http://dwarfstd.org/Dwarf3.pdf @unlink +/// @param data reference variable holding memory pointer to decode from +/// @param encoding dwarf encoding type +/// @returns decoded value +static +uintptr_t +readEncodedPointer(const uint8_t** data, uint8_t encoding) +{ + uintptr_t result = 0; + const uint8_t* p = *data; + if (encoding == DW_EH_PE_omit) + return result; + // first get value + switch (encoding & 0x0F) + { + case DW_EH_PE_absptr: + result = *((uintptr_t*)p); + p += sizeof(uintptr_t); + break; + case DW_EH_PE_uleb128: + result = readULEB128(&p); + break; + case DW_EH_PE_sleb128: + result = readSLEB128(&p); + break; + case DW_EH_PE_udata2: + result = *((uint16_t*)p); + p += sizeof(uint16_t); + break; + case DW_EH_PE_udata4: + result = *((uint32_t*)p); + p += sizeof(uint32_t); + break; + case DW_EH_PE_udata8: + result = *((uint64_t*)p); + p += sizeof(uint64_t); + break; + case DW_EH_PE_sdata2: + result = *((int16_t*)p); + p += sizeof(int16_t); + break; + case DW_EH_PE_sdata4: + result = *((int32_t*)p); + p += sizeof(int32_t); + break; + case DW_EH_PE_sdata8: + result = *((int64_t*)p); + p += sizeof(int64_t); + 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: + 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 (encoding & DW_EH_PE_indirect) + result = *((uintptr_t*)result); + *data = p; + return result; +} + +/// Deals with Dwarf actions matching our type infos +/// (OurExceptionType_t instances). Returns whether or not a dwarf emitted +/// action matches the supplied exception type. If such a match succeeds, +/// the handlerSwitchValue will be set with > 0 index value. Only +/// corresponding llvm.eh.selector type info arguments, cleanup arguments +/// are supported. Filters are not supported. +/// See Variable Length Data in: +/// @link http://dwarfstd.org/Dwarf3.pdf @unlink +/// Also see @link http://refspecs.freestandards.org/abi-eh-1.21.html @unlink +/// @param classInfo our array of type info pointers (to globals) +/// @param actionEntry index into above type info array or 0 (clean up). +/// We do not support filters. +/// @param exceptionObject thrown _Unwind_Exception instance. +/// @returns whether or not a type info was found. False is returned if only +/// a cleanup was found +static +bool +handleActionValue(const std::type_info** classInfo, uintptr_t actionEntry, + _Unwind_Exception* exceptionObject) +{ + __cxa_exception* excp = (__cxa_exception*)(exceptionObject+1) - 1; + const std::type_info* excpType = excp->exceptionType; + const uint8_t* actionPos = (uint8_t*)actionEntry; + for (int i = 0; true; ++i) + { + // Each emitted dwarf action corresponds to a 2 tuple of + // type info address offset, and action offset to the next + // emitted action. + int64_t typeOffset = readSLEB128(&actionPos); + const uint8_t* tempActionPos = actionPos; + int64_t actionOffset = readSLEB128(&tempActionPos); + assert((typeOffset >= 0) && "handleActionValue(...):filters are not supported."); + // Note: A typeOffset == 0 implies that a cleanup llvm.eh.selector + // argument has been matched. + if ((typeOffset > 0) && (excpType == classInfo[-typeOffset])) + { + excp->handlerSwitchValue = i + 1; + return true; + } + if (!actionOffset) + break; + actionPos += actionOffset; + } + return false; +} + // Return true if there is a handler and false otherwise // cache handlerSwitchValue, actionRecord, languageSpecificData, // catchTemp and adjustedPtr here. @@ -28,20 +231,92 @@ static bool contains_handler(_Unwind_Exception* exceptionObject, _Unwind_Context* context) { + const uint8_t* lsda = (const uint8_t*)_Unwind_GetLanguageSpecificData(context); + if (lsda) + { + // Get the current instruction pointer and offset it before next + // instruction in the current frame which threw the exception. + uintptr_t pc = _Unwind_GetIP(context) - 1; + // Get beginning current frame's code (as defined by the + // emitted dwarf code) + uintptr_t funcStart = _Unwind_GetRegionStart(context); + uintptr_t pcOffset = pc - funcStart; + const std::type_info** classInfo = NULL; + // Note: See JITDwarfEmitter::EmitExceptionTable(...) for corresponding + // dwarf emission + // Parse LSDA header. + uint8_t lpStartEncoding = *lsda++; + if (lpStartEncoding != DW_EH_PE_omit) + (void)readEncodedPointer(&lsda, lpStartEncoding); + uint8_t ttypeEncoding = *lsda++; + if (ttypeEncoding != DW_EH_PE_omit) + { + // Calculate type info locations in emitted dwarf code which + // were flagged by type info arguments to llvm.eh.selector + // intrinsic + uintptr_t classInfoOffset = readULEB128(&lsda); + classInfo = (const std::type_info**)(lsda + classInfoOffset); + } + // Walk call-site table looking for range that + // includes current PC. + uint8_t callSiteEncoding = *lsda++; + uint32_t callSiteTableLength = readULEB128(&lsda); + const uint8_t* callSiteTableStart = lsda; + const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength; + const uint8_t* actionTableStart = callSiteTableEnd; + const uint8_t* callSitePtr = callSiteTableStart; + __cxa_exception* excp = (__cxa_exception*)(exceptionObject+1) - 1; + while (callSitePtr < callSiteTableEnd) + { + uintptr_t start = readEncodedPointer(&callSitePtr, callSiteEncoding); + uintptr_t length = readEncodedPointer(&callSitePtr, callSiteEncoding); + uintptr_t landingPad = readEncodedPointer(&callSitePtr, callSiteEncoding); + // Note: Action value + uintptr_t actionEntry = readULEB128(&callSitePtr); + if (landingPad == 0) + continue; // no landing pad for this entry + if (actionEntry) + actionEntry += ((uintptr_t)actionTableStart) - 1; + bool exceptionMatched = false; + if ((start <= pcOffset) && (pcOffset < (start + length))) + { + excp->actionRecord = (const unsigned char*)(funcStart + landingPad); + if (actionEntry) + return handleActionValue(classInfo, + actionEntry, + exceptionObject); + // Note: Only non-clean up handlers are marked as + // found. Otherwise the clean up handlers will be + // re-found and executed during the clean up + // phase. + break; + } + } + } + return false; } -// return _URC_INSTALL_CONTEXT or _URC_FATAL_PHASE2_ERROR static _Unwind_Reason_Code -transfer_control_to_landing_pad(_Unwind_Context* context) +transfer_control_to_landing_pad(_Unwind_Exception* exceptionObject, + _Unwind_Context* context) { + __cxa_exception* excp = (__cxa_exception*)(exceptionObject+1) - 1; + _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)exceptionObject); + _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), excp->handlerSwitchValue); + _Unwind_SetIP(context, (uintptr_t)excp->actionRecord); + return _URC_INSTALL_CONTEXT; } -// return _URC_CONTINUE_UNWIND or _URC_FATAL_PHASE2_ERROR static _Unwind_Reason_Code -perform_cleanup(_Unwind_Context* context) +perform_cleanup(_Unwind_Exception* exceptionObject, _Unwind_Context* context) { + __cxa_exception* excp = (__cxa_exception*)(exceptionObject+1) - 1; + _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), (uintptr_t)exceptionObject); + _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0); + _Unwind_SetIP(context, (uintptr_t)excp->actionRecord); + return _URC_INSTALL_CONTEXT; } // public API @@ -68,7 +343,7 @@ __gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClas if (actions & _UA_CLEANUP_PHASE) return _URC_FATAL_PHASE1_ERROR; if (contains_handler(exceptionObject, context)) - return _URC_HANDLER_FOUND + return _URC_HANDLER_FOUND; return _URC_CONTINUE_UNWIND; } if (actions & _UA_CLEANUP_PHASE) @@ -76,10 +351,10 @@ __gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClas if (actions & _UA_HANDLER_FRAME) { // return _URC_INSTALL_CONTEXT or _URC_FATAL_PHASE2_ERROR - return transfer_control_to_landing_pad(context); + return transfer_control_to_landing_pad(exceptionObject, context); } // return _URC_CONTINUE_UNWIND or _URC_FATAL_PHASE2_ERROR - return perform_cleanup(); + return perform_cleanup(exceptionObject, context); } } else // foreign exception or force_unwind @@ -95,7 +370,7 @@ __gxx_personality_v0(int version, _Unwind_Action actions, uint64_t exceptionClas if (actions & _UA_HANDLER_FRAME) return _URC_FATAL_PHASE2_ERROR; // return _URC_CONTINUE_UNWIND or _URC_FATAL_PHASE2_ERROR - return perform_cleanup(); + return perform_cleanup(exceptionObject, context); } } } -- 2.7.4