331 lines · c
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "int_lib.h"10#include <stddef.h>11 12#include <unwind.h>13#if defined(__arm__) && !defined(__ARM_DWARF_EH__) && \14 !defined(__USING_SJLJ_EXCEPTIONS__)15// When building with older compilers (e.g. clang <3.9), it is possible that we16// have a version of unwind.h which does not provide the EHABI declarations17// which are quired for the C personality to conform to the specification. In18// order to provide forward compatibility for such compilers, we re-declare the19// necessary interfaces in the helper to permit a standalone compilation of the20// builtins (which contains the C unwinding personality for historical reasons).21#include "unwind-ehabi-helpers.h"22#endif23 24#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)25#include <windows.h>26#include <winnt.h>27 28EXCEPTION_DISPOSITION _GCC_specific_handler(PEXCEPTION_RECORD, void *, PCONTEXT,29 PDISPATCHER_CONTEXT,30 _Unwind_Personality_Fn);31#endif32 33#ifndef __has_feature34#define __has_feature(__feature) 035#endif36 37#if __has_feature(ptrauth_calls)38#include <ptrauth.h>39 40// `__ptrauth_restricted_intptr` is a feature of apple clang that predates41// support for direct application of `__ptrauth` to integer types. This42// guard is necessary to support compilation with those compiler.43#if __has_feature(ptrauth_restricted_intptr_qualifier)44#define __ptrauth_gcc_personality_intptr(key, addressDiscriminated, \45 discriminator) \46 __ptrauth_restricted_intptr(key, addressDiscriminated, discriminator)47#else48#define __ptrauth_gcc_personality_intptr(key, addressDiscriminated, \49 discriminator) \50 __ptrauth(key, addressDiscriminated, discriminator)51#endif52#else53#define __ptrauth_gcc_personality_intptr(...)54#endif55 56#define __ptrauth_gcc_personality_func_key ptrauth_key_function_pointer57 58// ptrauth_string_discriminator("__gcc_personality_v0'funcStart") == 0xDFEB59#define __ptrauth_gcc_personality_func_start \60 __ptrauth_gcc_personality_intptr(__ptrauth_gcc_personality_func_key, 1, \61 0xDFEB)62 63// ptrauth_string_discriminator("__gcc_personality_v0'start") == 0x52DC64#define __ptrauth_gcc_personality_start \65 __ptrauth_gcc_personality_intptr(__ptrauth_gcc_personality_func_key, 1, \66 0x52DC)67 68// ptrauth_string_discriminator("__gcc_personality_v0'length") == 0xFFF769#define __ptrauth_gcc_personality_length \70 __ptrauth_gcc_personality_intptr(__ptrauth_gcc_personality_func_key, 1, \71 0xFFF7)72 73// ptrauth_string_discriminator("__gcc_personality_v0'landingPadOffset") ==74// 0x649875#define __ptrauth_gcc_personality_lpoffset \76 __ptrauth_gcc_personality_intptr(__ptrauth_gcc_personality_func_key, 1, \77 0x6498)78 79// ptrauth_string_discriminator("__gcc_personality_v0'landingPad") == 0xA13480#define __ptrauth_gcc_personality_lpad_disc 0xA13481#define __ptrauth_gcc_personality_lpad \82 __ptrauth_gcc_personality_intptr(__ptrauth_gcc_personality_func_key, 1, \83 __ptrauth_gcc_personality_lpad_disc)84 85// Pointer encodings documented at:86// http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html87 88#define DW_EH_PE_omit 0xff // no data follows89 90#define DW_EH_PE_absptr 0x0091#define DW_EH_PE_uleb128 0x0192#define DW_EH_PE_udata2 0x0293#define DW_EH_PE_udata4 0x0394#define DW_EH_PE_udata8 0x0495#define DW_EH_PE_sleb128 0x0996#define DW_EH_PE_sdata2 0x0A97#define DW_EH_PE_sdata4 0x0B98#define DW_EH_PE_sdata8 0x0C99 100#define DW_EH_PE_pcrel 0x10101#define DW_EH_PE_textrel 0x20102#define DW_EH_PE_datarel 0x30103#define DW_EH_PE_funcrel 0x40104#define DW_EH_PE_aligned 0x50105#define DW_EH_PE_indirect 0x80 // gcc extension106 107// read a uleb128 encoded value and advance pointer108static size_t readULEB128(const uint8_t **data) {109 size_t result = 0;110 size_t shift = 0;111 unsigned char byte;112 const uint8_t *p = *data;113 do {114 byte = *p++;115 result |= (byte & 0x7f) << shift;116 shift += 7;117 } while (byte & 0x80);118 *data = p;119 return result;120}121 122// read a pointer encoded value and advance pointer123static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) {124 const uint8_t *p = *data;125 uintptr_t result = 0;126 127 if (encoding == DW_EH_PE_omit)128 return 0;129 130 // first get value131 switch (encoding & 0x0F) {132 case DW_EH_PE_absptr:133 result = *((const uintptr_t *)p);134 p += sizeof(uintptr_t);135 break;136 case DW_EH_PE_uleb128:137 result = readULEB128(&p);138 break;139 case DW_EH_PE_udata2:140 result = *((const uint16_t *)p);141 p += sizeof(uint16_t);142 break;143 case DW_EH_PE_udata4:144 result = *((const uint32_t *)p);145 p += sizeof(uint32_t);146 break;147 case DW_EH_PE_udata8:148 result = *((const uint64_t *)p);149 p += sizeof(uint64_t);150 break;151 case DW_EH_PE_sdata2:152 result = *((const int16_t *)p);153 p += sizeof(int16_t);154 break;155 case DW_EH_PE_sdata4:156 result = *((const int32_t *)p);157 p += sizeof(int32_t);158 break;159 case DW_EH_PE_sdata8:160 result = *((const int64_t *)p);161 p += sizeof(int64_t);162 break;163 case DW_EH_PE_sleb128:164 default:165 // not supported166 compilerrt_abort();167 break;168 }169 170 // then add relative offset171 switch (encoding & 0x70) {172 case DW_EH_PE_absptr:173 // do nothing174 break;175 case DW_EH_PE_pcrel:176 result += (uintptr_t)(*data);177 break;178 case DW_EH_PE_textrel:179 case DW_EH_PE_datarel:180 case DW_EH_PE_funcrel:181 case DW_EH_PE_aligned:182 default:183 // not supported184 compilerrt_abort();185 break;186 }187 188 // then apply indirection189 if (encoding & DW_EH_PE_indirect) {190 result = *((const uintptr_t *)result);191 }192 193 *data = p;194 return result;195}196 197#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) && \198 !defined(__ARM_DWARF_EH__) && !defined(__SEH__)199#define USING_ARM_EHABI 1200_Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception *,201 struct _Unwind_Context *);202#endif203 204static inline _Unwind_Reason_Code205continueUnwind(struct _Unwind_Exception *exceptionObject,206 struct _Unwind_Context *context) {207#if USING_ARM_EHABI208 // On ARM EHABI the personality routine is responsible for actually209 // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).210 if (__gnu_unwind_frame(exceptionObject, context) != _URC_OK)211 return _URC_FAILURE;212#endif213 return _URC_CONTINUE_UNWIND;214}215 216// The C compiler makes references to __gcc_personality_v0 in217// the dwarf unwind information for translation units that use218// __attribute__((cleanup(xx))) on local variables.219// This personality routine is called by the system unwinder220// on each frame as the stack is unwound during a C++ exception221// throw through a C function compiled with -fexceptions.222#if __USING_SJLJ_EXCEPTIONS__223// the setjump-longjump based exceptions personality routine has a224// different name225COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_sj0(226 int version, _Unwind_Action actions, uint64_t exceptionClass,227 struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)228#elif USING_ARM_EHABI229// The ARM EHABI personality routine has a different signature.230COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(231 _Unwind_State state, struct _Unwind_Exception *exceptionObject,232 struct _Unwind_Context *context)233#elif defined(__SEH__)234static _Unwind_Reason_Code __gcc_personality_imp(235 int version, _Unwind_Action actions, uint64_t exceptionClass,236 struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)237#else238COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(239 int version, _Unwind_Action actions, uint64_t exceptionClass,240 struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)241#endif242{243 // Since C does not have catch clauses, there is nothing to do during244 // phase 1 (the search phase).245#if USING_ARM_EHABI246 // After resuming from a cleanup we should also continue on to the next247 // frame straight away.248 if ((state & _US_ACTION_MASK) != _US_UNWIND_FRAME_STARTING)249#else250 if (actions & _UA_SEARCH_PHASE)251#endif252 return continueUnwind(exceptionObject, context);253 254 // There is nothing to do if there is no LSDA for this frame.255 const uint8_t *lsda = (uint8_t *)_Unwind_GetLanguageSpecificData(context);256 if (lsda == (uint8_t *)0)257 return continueUnwind(exceptionObject, context);258 259 uintptr_t pc = (uintptr_t)_Unwind_GetIP(context) - 1;260 uintptr_t __ptrauth_gcc_personality_func_start funcStart =261 (uintptr_t)_Unwind_GetRegionStart(context);262 uintptr_t pcOffset = pc - funcStart;263 264 // Parse LSDA header.265 uint8_t lpStartEncoding = *lsda++;266 if (lpStartEncoding != DW_EH_PE_omit) {267 readEncodedPointer(&lsda, lpStartEncoding);268 }269 uint8_t ttypeEncoding = *lsda++;270 if (ttypeEncoding != DW_EH_PE_omit) {271 readULEB128(&lsda);272 }273 // Walk call-site table looking for range that includes current PC.274 uint8_t callSiteEncoding = *lsda++;275 size_t callSiteTableLength = readULEB128(&lsda);276 const uint8_t *callSiteTableStart = lsda;277 const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength;278 const uint8_t *p = callSiteTableStart;279 while (p < callSiteTableEnd) {280 uintptr_t __ptrauth_gcc_personality_start start =281 readEncodedPointer(&p, callSiteEncoding);282 size_t __ptrauth_gcc_personality_length length =283 readEncodedPointer(&p, callSiteEncoding);284 size_t __ptrauth_gcc_personality_lpoffset landingPadOffset =285 readEncodedPointer(&p, callSiteEncoding);286 readULEB128(&p); // action value not used for C code287 if (landingPadOffset == 0)288 continue; // no landing pad for this entry289 if ((start <= pcOffset) && (pcOffset < (start + length))) {290 // Found landing pad for the PC.291 // Set Instruction Pointer to so we re-enter function292 // at landing pad. The landing pad is created by the compiler293 // to take two parameters in registers.294 _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),295 (uintptr_t)exceptionObject);296 _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);297 size_t __ptrauth_gcc_personality_lpad landingPad =298 funcStart + landingPadOffset;299#if __has_feature(ptrauth_calls)300 uintptr_t stackPointer = _Unwind_GetGR(context, -2);301 const uintptr_t existingDiscriminator = ptrauth_blend_discriminator(302 &landingPad, __ptrauth_gcc_personality_lpad_disc);303 // newIP is authenticated as if it were qualified with a pseudo qualifier304 // along the lines of:305 // __ptrauth(ptrauth_key_return_address, <stackPointer>, 0)306 // where the stack pointer is used in place of the strict storage307 // address.308 uintptr_t newIP = (uintptr_t)ptrauth_auth_and_resign(309 *(void **)&landingPad, __ptrauth_gcc_personality_func_key,310 existingDiscriminator, ptrauth_key_return_address, stackPointer);311 _Unwind_SetIP(context, newIP);312#else313 _Unwind_SetIP(context, landingPad);314#endif315 return _URC_INSTALL_CONTEXT;316 }317 }318 319 // No landing pad found, continue unwinding.320 return continueUnwind(exceptionObject, context);321}322 323#if defined(__SEH__) && !defined(__USING_SJLJ_EXCEPTIONS__)324COMPILER_RT_ABI EXCEPTION_DISPOSITION325__gcc_personality_seh0(PEXCEPTION_RECORD ms_exc, void *this_frame,326 PCONTEXT ms_orig_context, PDISPATCHER_CONTEXT ms_disp) {327 return _GCC_specific_handler(ms_exc, this_frame, ms_orig_context, ms_disp,328 __gcc_personality_imp);329}330#endif331