brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.0 KiB · 6d92a7b Raw
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