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 46static 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 61static 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 142static inline _Unwind_Reason_Code 143continueUnwind(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 163COMPILER_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. 168COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0( 169 _Unwind_State state, struct _Unwind_Exception *exceptionObject, 170 struct _Unwind_Context *context) 171#else 172COMPILER_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