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