reference, declarationdefinition
definition → references, declarations, derived classes, virtual overrides
reference to multiple definitions → definitions
unreferenced
    1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
//===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#include "int_lib.h"

#include <unwind.h>
#if defined(__arm__) && !defined(__ARM_DWARF_EH__) &&                          \
    !defined(__USING_SJLJ_EXCEPTIONS__)
// When building with older compilers (e.g. clang <3.9), it is possible that we
// have a version of unwind.h which does not provide the EHABI declarations
// which are quired for the C personality to conform to the specification.  In
// order to provide forward compatibility for such compilers, we re-declare the
// necessary interfaces in the helper to permit a standalone compilation of the
// builtins (which contains the C unwinding personality for historical reasons).
#include "unwind-ehabi-helpers.h"
#endif

// Pointer encodings documented at:
//   http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html

#define DW_EH_PE_omit 0xff // no data follows

#define DW_EH_PE_absptr 0x00
#define DW_EH_PE_uleb128 0x01
#define DW_EH_PE_udata2 0x02
#define DW_EH_PE_udata4 0x03
#define DW_EH_PE_udata8 0x04
#define DW_EH_PE_sleb128 0x09
#define DW_EH_PE_sdata2 0x0A
#define DW_EH_PE_sdata4 0x0B
#define DW_EH_PE_sdata8 0x0C

#define DW_EH_PE_pcrel 0x10
#define DW_EH_PE_textrel 0x20
#define DW_EH_PE_datarel 0x30
#define DW_EH_PE_funcrel 0x40
#define DW_EH_PE_aligned 0x50
#define DW_EH_PE_indirect 0x80 // gcc extension

// read a uleb128 encoded value and advance pointer
static uintptr_t readULEB128(const uint8_t **data) {
  uintptr_t result = 0;
  uintptr_t shift = 0;
  unsigned char byte;
  const uint8_t *p = *data;
  do {
    byte = *p++;
    result |= (byte & 0x7f) << shift;
    shift += 7;
  } while (byte & 0x80);
  *data = p;
  return result;
}

// read a pointer encoded value and advance pointer
static uintptr_t readEncodedPointer(const uint8_t **data, uint8_t encoding) {
  const uint8_t *p = *data;
  uintptr_t result = 0;

  if (encoding == DW_EH_PE_omit)
    return 0;

  // first get value
  switch (encoding & 0x0F) {
  case DW_EH_PE_absptr:
    result = *((const uintptr_t *)p);
    p += sizeof(uintptr_t);
    break;
  case DW_EH_PE_uleb128:
    result = readULEB128(&p);
    break;
  case DW_EH_PE_udata2:
    result = *((const uint16_t *)p);
    p += sizeof(uint16_t);
    break;
  case DW_EH_PE_udata4:
    result = *((const uint32_t *)p);
    p += sizeof(uint32_t);
    break;
  case DW_EH_PE_udata8:
    result = *((const uint64_t *)p);
    p += sizeof(uint64_t);
    break;
  case DW_EH_PE_sdata2:
    result = *((const int16_t *)p);
    p += sizeof(int16_t);
    break;
  case DW_EH_PE_sdata4:
    result = *((const int32_t *)p);
    p += sizeof(int32_t);
    break;
  case DW_EH_PE_sdata8:
    result = *((const int64_t *)p);
    p += sizeof(int64_t);
    break;
  case DW_EH_PE_sleb128:
  default:
    // not supported
    compilerrt_abort();
    break;
  }

  // then add relative offset
  switch (encoding & 0x70) {
  case DW_EH_PE_absptr:
    // do nothing
    break;
  case DW_EH_PE_pcrel:
    result += (uintptr_t)(*data);
    break;
  case DW_EH_PE_textrel:
  case DW_EH_PE_datarel:
  case DW_EH_PE_funcrel:
  case DW_EH_PE_aligned:
  default:
    // not supported
    compilerrt_abort();
    break;
  }

  // then apply indirection
  if (encoding & DW_EH_PE_indirect) {
    result = *((const uintptr_t *)result);
  }

  *data = p;
  return result;
}

#if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) &&                 \
    !defined(__ARM_DWARF_EH__)
#define USING_ARM_EHABI 1
_Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception *,
                                       struct _Unwind_Context *);
#endif

static inline _Unwind_Reason_Code
continueUnwind(struct _Unwind_Exception *exceptionObject,
               struct _Unwind_Context *context) {
#if USING_ARM_EHABI
  // On ARM EHABI the personality routine is responsible for actually
  // unwinding a single stack frame before returning (ARM EHABI Sec. 6.1).
  if (__gnu_unwind_frame(exceptionObject, context) != _URC_OK)
    return _URC_FAILURE;
#endif
  return _URC_CONTINUE_UNWIND;
}

// The C compiler makes references to __gcc_personality_v0 in
// the dwarf unwind information for translation units that use
// __attribute__((cleanup(xx))) on local variables.
// This personality routine is called by the system unwinder
// on each frame as the stack is unwound during a C++ exception
// throw through a C function compiled with -fexceptions.
#if __USING_SJLJ_EXCEPTIONS__
// the setjump-longjump based exceptions personality routine has a
// different name
COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_sj0(
    int version, _Unwind_Action actions, uint64_t exceptionClass,
    struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
#elif USING_ARM_EHABI
// The ARM EHABI personality routine has a different signature.
COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
    _Unwind_State state, struct _Unwind_Exception *exceptionObject,
    struct _Unwind_Context *context)
#else
COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
    int version, _Unwind_Action actions, uint64_t exceptionClass,
    struct _Unwind_Exception *exceptionObject, struct _Unwind_Context *context)
#endif
{
  // Since C does not have catch clauses, there is nothing to do during
  // phase 1 (the search phase).
#if USING_ARM_EHABI
  // After resuming from a cleanup we should also continue on to the next
  // frame straight away.
  if ((state & _US_ACTION_MASK) != _US_UNWIND_FRAME_STARTING)
#else
  if (actions & _UA_SEARCH_PHASE)
#endif
    return continueUnwind(exceptionObject, context);

  // There is nothing to do if there is no LSDA for this frame.
  const uint8_t *lsda = (uint8_t *)_Unwind_GetLanguageSpecificData(context);
  if (lsda == (uint8_t *)0)
    return continueUnwind(exceptionObject, context);

  uintptr_t pc = (uintptr_t)_Unwind_GetIP(context) - 1;
  uintptr_t funcStart = (uintptr_t)_Unwind_GetRegionStart(context);
  uintptr_t pcOffset = pc - funcStart;

  // Parse LSDA header.
  uint8_t lpStartEncoding = *lsda++;
  if (lpStartEncoding != DW_EH_PE_omit) {
    readEncodedPointer(&lsda, lpStartEncoding);
  }
  uint8_t ttypeEncoding = *lsda++;
  if (ttypeEncoding != DW_EH_PE_omit) {
    readULEB128(&lsda);
  }
  // Walk call-site table looking for range that includes current PC.
  uint8_t callSiteEncoding = *lsda++;
  uint32_t callSiteTableLength = readULEB128(&lsda);
  const uint8_t *callSiteTableStart = lsda;
  const uint8_t *callSiteTableEnd = callSiteTableStart + callSiteTableLength;
  const uint8_t *p = callSiteTableStart;
  while (p < callSiteTableEnd) {
    uintptr_t start = readEncodedPointer(&p, callSiteEncoding);
    uintptr_t length = readEncodedPointer(&p, callSiteEncoding);
    uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding);
    readULEB128(&p); // action value not used for C code
    if (landingPad == 0)
      continue; // no landing pad for this entry
    if ((start <= pcOffset) && (pcOffset < (start + length))) {
      // Found landing pad for the PC.
      // Set Instruction Pointer to so we re-enter function
      // at landing pad. The landing pad is created by the compiler
      // to take two parameters in registers.
      _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
                    (uintptr_t)exceptionObject);
      _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
      _Unwind_SetIP(context, (funcStart + landingPad));
      return _URC_INSTALL_CONTEXT;
    }
  }

  // No landing pad found, continue unwinding.
  return continueUnwind(exceptionObject, context);
}