1/* ===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------=== 2 * 3 * The LLVM Compiler Infrastructure 4 * 5 * This file is dual licensed under the MIT and the University of Illinois Open 6 * Source Licenses. See LICENSE.TXT for details. 7 * 8 * ===----------------------------------------------------------------------=== 9 * 10 */ 11 12#include "int_lib.h" 13 14#include <unwind.h> 15 16/* 17 * Pointer encodings documented at: 18 * http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html 19 */ 20 21#define DW_EH_PE_omit 0xff /* no data follows */ 22 23#define DW_EH_PE_absptr 0x00 24#define DW_EH_PE_uleb128 0x01 25#define DW_EH_PE_udata2 0x02 26#define DW_EH_PE_udata4 0x03 27#define DW_EH_PE_udata8 0x04 28#define DW_EH_PE_sleb128 0x09 29#define DW_EH_PE_sdata2 0x0A 30#define DW_EH_PE_sdata4 0x0B 31#define DW_EH_PE_sdata8 0x0C 32 33#define DW_EH_PE_pcrel 0x10 34#define DW_EH_PE_textrel 0x20 35#define DW_EH_PE_datarel 0x30 36#define DW_EH_PE_funcrel 0x40 37#define DW_EH_PE_aligned 0x50 38#define DW_EH_PE_indirect 0x80 /* gcc extension */ 39 40 41 42/* read a uleb128 encoded value and advance pointer */ 43static uintptr_t readULEB128(const uint8_t** data) 44{ 45 uintptr_t result = 0; 46 uintptr_t shift = 0; 47 unsigned char byte; 48 const uint8_t* p = *data; 49 do { 50 byte = *p++; 51 result |= (byte & 0x7f) << shift; 52 shift += 7; 53 } while (byte & 0x80); 54 *data = p; 55 return result; 56} 57 58/* read a pointer encoded value and advance pointer */ 59static uintptr_t readEncodedPointer(const uint8_t** data, uint8_t encoding) 60{ 61 const uint8_t* p = *data; 62 uintptr_t result = 0; 63 64 if ( encoding == DW_EH_PE_omit ) 65 return 0; 66 67 /* first get value */ 68 switch (encoding & 0x0F) { 69 case DW_EH_PE_absptr: 70 result = *((const uintptr_t*)p); 71 p += sizeof(uintptr_t); 72 break; 73 case DW_EH_PE_uleb128: 74 result = readULEB128(&p); 75 break; 76 case DW_EH_PE_udata2: 77 result = *((const uint16_t*)p); 78 p += sizeof(uint16_t); 79 break; 80 case DW_EH_PE_udata4: 81 result = *((const uint32_t*)p); 82 p += sizeof(uint32_t); 83 break; 84 case DW_EH_PE_udata8: 85 result = *((const uint64_t*)p); 86 p += sizeof(uint64_t); 87 break; 88 case DW_EH_PE_sdata2: 89 result = *((const int16_t*)p); 90 p += sizeof(int16_t); 91 break; 92 case DW_EH_PE_sdata4: 93 result = *((const int32_t*)p); 94 p += sizeof(int32_t); 95 break; 96 case DW_EH_PE_sdata8: 97 result = *((const int64_t*)p); 98 p += sizeof(int64_t); 99 break; 100 case DW_EH_PE_sleb128: 101 default: 102 /* not supported */ 103 compilerrt_abort(); 104 break; 105 } 106 107 /* then add relative offset */ 108 switch ( encoding & 0x70 ) { 109 case DW_EH_PE_absptr: 110 /* do nothing */ 111 break; 112 case DW_EH_PE_pcrel: 113 result += (uintptr_t)(*data); 114 break; 115 case DW_EH_PE_textrel: 116 case DW_EH_PE_datarel: 117 case DW_EH_PE_funcrel: 118 case DW_EH_PE_aligned: 119 default: 120 /* not supported */ 121 compilerrt_abort(); 122 break; 123 } 124 125 /* then apply indirection */ 126 if (encoding & DW_EH_PE_indirect) { 127 result = *((const uintptr_t*)result); 128 } 129 130 *data = p; 131 return result; 132} 133 134#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ 135 !defined(__ARM_DWARF_EH__) 136#define USING_ARM_EHABI 1 137_Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception *, 138 struct _Unwind_Context *); 139#endif 140 141static inline _Unwind_Reason_Code 142continueUnwind(struct _Unwind_Exception *exceptionObject, 143 struct _Unwind_Context *context) { 144#if USING_ARM_EHABI 145 /* 146 * On ARM EHABI the personality routine is responsible for actually 147 * unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). 148 */ 149 if (__gnu_unwind_frame(exceptionObject, context) != _URC_OK) 150 return _URC_FAILURE; 151#endif 152 return _URC_CONTINUE_UNWIND; 153} 154 155/* 156 * The C compiler makes references to __gcc_personality_v0 in 157 * the dwarf unwind information for translation units that use 158 * __attribute__((cleanup(xx))) on local variables. 159 * This personality routine is called by the system unwinder 160 * on each frame as the stack is unwound during a C++ exception 161 * throw through a C function compiled with -fexceptions. 162 */ 163#if __USING_SJLJ_EXCEPTIONS__ 164/* the setjump-longjump based exceptions personality routine has a 165 * different name */ 166COMPILER_RT_ABI _Unwind_Reason_Code 167__gcc_personality_sj0(int version, _Unwind_Action actions, 168 uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject, 169 struct _Unwind_Context *context) 170#elif USING_ARM_EHABI 171/* The ARM EHABI personality routine has a different signature. */ 172COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0( 173 _Unwind_State state, struct _Unwind_Exception *exceptionObject, 174 struct _Unwind_Context *context) 175#else 176COMPILER_RT_ABI _Unwind_Reason_Code 177__gcc_personality_v0(int version, _Unwind_Action actions, 178 uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject, 179 struct _Unwind_Context *context) 180#endif 181{ 182 /* Since C does not have catch clauses, there is nothing to do during */ 183 /* phase 1 (the search phase). */ 184#if USING_ARM_EHABI 185 /* After resuming from a cleanup we should also continue on to the next 186 * frame straight away. */ 187 if ((state & _US_ACTION_MASK) != _US_UNWIND_FRAME_STARTING) 188#else 189 if ( actions & _UA_SEARCH_PHASE ) 190#endif 191 return continueUnwind(exceptionObject, context); 192 193 /* There is nothing to do if there is no LSDA for this frame. */ 194 const uint8_t* lsda = (uint8_t*)_Unwind_GetLanguageSpecificData(context); 195 if ( lsda == (uint8_t*) 0 ) 196 return continueUnwind(exceptionObject, context); 197 198 uintptr_t pc = _Unwind_GetIP(context)-1; 199 uintptr_t funcStart = _Unwind_GetRegionStart(context); 200 uintptr_t pcOffset = pc - funcStart; 201 202 /* Parse LSDA header. */ 203 uint8_t lpStartEncoding = *lsda++; 204 if (lpStartEncoding != DW_EH_PE_omit) { 205 readEncodedPointer(&lsda, lpStartEncoding); 206 } 207 uint8_t ttypeEncoding = *lsda++; 208 if (ttypeEncoding != DW_EH_PE_omit) { 209 readULEB128(&lsda); 210 } 211 /* Walk call-site table looking for range that includes current PC. */ 212 uint8_t callSiteEncoding = *lsda++; 213 uint32_t callSiteTableLength = readULEB128(&lsda); 214 const uint8_t* callSiteTableStart = lsda; 215 const uint8_t* callSiteTableEnd = callSiteTableStart + callSiteTableLength; 216 const uint8_t* p=callSiteTableStart; 217 while (p < callSiteTableEnd) { 218 uintptr_t start = readEncodedPointer(&p, callSiteEncoding); 219 uintptr_t length = readEncodedPointer(&p, callSiteEncoding); 220 uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding); 221 readULEB128(&p); /* action value not used for C code */ 222 if ( landingPad == 0 ) 223 continue; /* no landing pad for this entry */ 224 if ( (start <= pcOffset) && (pcOffset < (start+length)) ) { 225 /* Found landing pad for the PC. 226 * Set Instruction Pointer to so we re-enter function 227 * at landing pad. The landing pad is created by the compiler 228 * to take two parameters in registers. 229 */ 230 _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), 231 (uintptr_t)exceptionObject); 232 _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0); 233 _Unwind_SetIP(context, (funcStart + landingPad)); 234 return _URC_INSTALL_CONTEXT; 235 } 236 } 237 238 /* No landing pad found, continue unwinding. */ 239 return continueUnwind(exceptionObject, context); 240} 241 242