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