1 //===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "int_lib.h" 10 #include <stddef.h> 11 12 #include <unwind.h> 13 /* 14 * XXX On FreeBSD, this file is compiled into three libraries: 15 * - libcompiler_rt 16 * - libgcc_eh 17 * - libgcc_s 18 * 19 * In the former, the include path points to the contrib/libcxxrt/unwind-arm.h 20 * copy of unwind.h. In the latter, the include path points to the 21 * contrib/libunwind/include/unwind.h header (LLVM libunwind). 22 * 23 * Neither (seemingly redundant) variant of unwind.h needs the redefinitions 24 * provided in the "helpful" header below, and libcxxrt's unwind-arm.h provides 25 * *no* useful distinguishing macros, so just forcibly disable the helper 26 * header on FreeBSD. 27 */ 28 #if defined(__arm__) && !defined(__ARM_DWARF_EH__) && \ 29 !defined(__USING_SJLJ_EXCEPTIONS__) && !defined(__FreeBSD__) 30 // When building with older compilers (e.g. clang <3.9), it is possible that we 31 // have a version of unwind.h which does not provide the EHABI declarations 32 // which are quired for the C personality to conform to the specification. In 33 // order to provide forward compatibility for such compilers, we re-declare the 34 // necessary interfaces in the helper to permit a standalone compilation of the 35 // builtins (which contains the C unwinding personality for historical reasons). 36 #include "unwind-ehabi-helpers.h" 37 #endif 38 39 #if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) 40 #include <windows.h> 41 #include <winnt.h> 42 43 EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, void *, PCONTEXT, 44 PDISPATCHER_CONTEXT, 45 _Unwind_Personality_Fn); 46 #endif 47 48 // Pointer encodings documented at: 49 // http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html 50 51 #define DW_EH_PE_omit 0xff // no data follows 52 53 #define DW_EH_PE_absptr 0x00 54 #define DW_EH_PE_uleb128 0x01 55 #define DW_EH_PE_udata2 0x02 56 #define DW_EH_PE_udata4 0x03 57 #define DW_EH_PE_udata8 0x04 58 #define DW_EH_PE_sleb128 0x09 59 #define DW_EH_PE_sdata2 0x0A 60 #define DW_EH_PE_sdata4 0x0B 61 #define DW_EH_PE_sdata8 0x0C 62 63 #define DW_EH_PE_pcrel 0x10 64 #define DW_EH_PE_textrel 0x20 65 #define DW_EH_PE_datarel 0x30 66 #define DW_EH_PE_funcrel 0x40 67 #define DW_EH_PE_aligned 0x50 68 #define DW_EH_PE_indirect 0x80 // gcc extension 69 70 // read a uleb128 encoded value and advance pointer 71 static size_t readULEB128(const uint8_t **data) { 72 size_t result = 0; 73 size_t shift = 0; 74 unsigned char byte; 75 const uint8_t *p = *data; 76 do { 77 byte = *p++; 78 result |= (byte & 0x7f) << shift; 79 shift += 7; 80 } while (byte & 0x80); 81 *data = p; 82 return result; 83 } 84 85 // read a pointer encoded value and advance pointer 86 static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) { 87 const uint8_t *p = *data; 88 uintptr_t result = 0; 89 90 if (encoding == DW_EH_PE_omit) 91 return 0; 92 93 // first get value 94 switch (encoding & 0x0F) { 95 case DW_EH_PE_absptr: 96 result = *((const uintptr_t *)p); 97 p += sizeof(uintptr_t); 98 break; 99 case DW_EH_PE_uleb128: 100 result = readULEB128(&p); 101 break; 102 case DW_EH_PE_udata2: 103 result = *((const uint16_t *)p); 104 p += sizeof(uint16_t); 105 break; 106 case DW_EH_PE_udata4: 107 result = *((const uint32_t *)p); 108 p += sizeof(uint32_t); 109 break; 110 case DW_EH_PE_udata8: 111 result = *((const uint64_t *)p); 112 p += sizeof(uint64_t); 113 break; 114 case DW_EH_PE_sdata2: 115 result = *((const int16_t *)p); 116 p += sizeof(int16_t); 117 break; 118 case DW_EH_PE_sdata4: 119 result = *((const int32_t *)p); 120 p += sizeof(int32_t); 121 break; 122 case DW_EH_PE_sdata8: 123 result = *((const int64_t *)p); 124 p += sizeof(int64_t); 125 break; 126 case DW_EH_PE_sleb128: 127 default: 128 // not supported 129 compilerrt_abort(); 130 break; 131 } 132 133 // then add relative offset 134 switch (encoding & 0x70) { 135 case DW_EH_PE_absptr: 136 // do nothing 137 break; 138 case DW_EH_PE_pcrel: 139 result += (uintptr_t)(*data); 140 break; 141 case DW_EH_PE_textrel: 142 case DW_EH_PE_datarel: 143 case DW_EH_PE_funcrel: 144 case DW_EH_PE_aligned: 145 default: 146 // not supported 147 compilerrt_abort(); 148 break; 149 } 150 151 // then apply indirection 152 if (encoding & DW_EH_PE_indirect) { 153 result = *((const uintptr_t *)result); 154 } 155 156 *data = p; 157 return result; 158 } 159 160 #if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \ 161 !defined(__ARM_DWARF_EH__) && !defined(__SEH__) 162 #define USING_ARM_EHABI 1 163 _Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception *, 164 struct _Unwind_Context *); 165 #endif 166 167 static inline _Unwind_Reason_Code 168 continueUnwind(struct _Unwind_Exception *exceptionObject, 169 struct _Unwind_Context *context) { 170 #if USING_ARM_EHABI 171 // On ARM EHABI the personality routine is responsible for actually 172 // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1). 173 if (__gnu_unwind_frame(exceptionObject, context) != _URC_OK) 174 return _URC_FAILURE; 175 #endif 176 return _URC_CONTINUE_UNWIND; 177 } 178 179 // The C compiler makes references to __gcc_personality_v0 in 180 // the dwarf unwind information for translation units that use 181 // __attribute__((cleanup(xx))) on local variables. 182 // This personality routine is called by the system unwinder 183 // on each frame as the stack is unwound during a C++ exception 184 // throw through a C function compiled with -fexceptions. 185 #if __USING_SJLJ_EXCEPTIONS__ 186 // the setjump-longjump based exceptions personality routine has a 187 // different name 188 COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_sj0( 189 int version, _Unwind_Action actions, uint64_t exceptionClass, 190 struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context) 191 #elif USING_ARM_EHABI 192 // The ARM EHABI personality routine has a different signature. 193 COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0( 194 _Unwind_State state, struct _Unwind_Exception *exceptionObject, 195 struct _Unwind_Context *context) 196 #elif defined(__SEH__) 197 static _Unwind_Reason_Code __gcc_personality_imp( 198 int version, _Unwind_Action actions, uint64_t exceptionClass, 199 struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context) 200 #else 201 COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0( 202 int version, _Unwind_Action actions, uint64_t exceptionClass, 203 struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context) 204 #endif 205 { 206 // Since C does not have catch clauses, there is nothing to do during 207 // phase 1 (the search phase). 208 #if USING_ARM_EHABI 209 // After resuming from a cleanup we should also continue on to the next 210 // frame straight away. 211 if ((state & _US_ACTION_MASK) != _US_UNWIND_FRAME_STARTING) 212 #else 213 if (actions & _UA_SEARCH_PHASE) 214 #endif 215 return continueUnwind(exceptionObject, context); 216 217 // There is nothing to do if there is no LSDA for this frame. 218 const uint8_t *lsda = (uint8_t *)_Unwind_GetLanguageSpecificData(context); 219 if (lsda == (uint8_t *)0) 220 return continueUnwind(exceptionObject, context); 221 222 uintptr_t pc = (uintptr_t)_Unwind_GetIP(context) - 1; 223 uintptr_t funcStart = (uintptr_t)_Unwind_GetRegionStart(context); 224 uintptr_t pcOffset = pc - funcStart; 225 226 // Parse LSDA header. 227 uint8_t lpStartEncoding = *lsda++; 228 if (lpStartEncoding != DW_EH_PE_omit) { 229 readEncodedPointer(&lsda, lpStartEncoding); 230 } 231 uint8_t ttypeEncoding = *lsda++; 232 if (ttypeEncoding != DW_EH_PE_omit) { 233 readULEB128(&lsda); 234 } 235 // Walk call-site table looking for range that includes current PC. 236 uint8_t callSiteEncoding = *lsda++; 237 size_t callSiteTableLength = readULEB128(&lsda); 238 const uint8_t *callSiteTableStart = lsda; 239 const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength; 240 const uint8_t *p = callSiteTableStart; 241 while (p < callSiteTableEnd) { 242 uintptr_t start = readEncodedPointer(&p, callSiteEncoding); 243 size_t length = readEncodedPointer(&p, callSiteEncoding); 244 size_t landingPad = readEncodedPointer(&p, callSiteEncoding); 245 readULEB128(&p); // action value not used for C code 246 if (landingPad == 0) 247 continue; // no landing pad for this entry 248 if ((start <= pcOffset) && (pcOffset < (start + length))) { 249 // Found landing pad for the PC. 250 // Set Instruction Pointer to so we re-enter function 251 // at landing pad. The landing pad is created by the compiler 252 // to take two parameters in registers. 253 _Unwind_SetGR(context, __builtin_eh_return_data_regno(0), 254 (uintptr_t)exceptionObject); 255 _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0); 256 _Unwind_SetIP(context, (funcStart + landingPad)); 257 return _URC_INSTALL_CONTEXT; 258 } 259 } 260 261 // No landing pad found, continue unwinding. 262 return continueUnwind(exceptionObject, context); 263 } 264 265 #if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__) 266 COMPILER_RT_ABI EXCEPTION_DISPOSITION 267 __gcc_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame, 268 PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp) { 269 return _GCC_specific_handler(ms_exc, this_frame, ms_orig_context, ms_disp, 270 __gcc_personality_imp); 271 } 272 #endif 273