| 12
 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
 235
 236
 237
 238
 239
 240
 241
 242
 243
 244
 245
 246
 247
 248
 249
 250
 251
 252
 
 | /* ===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------===
 *
 *      	       The LLVM Compiler Infrastructure
 *
 * This file is dual licensed under the MIT and the University of Illinois Open
 * Source Licenses. See LICENSE.TXT for details.
 *
 * ===----------------------------------------------------------------------===
 *
 */
#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 = _Unwind_GetIP(context)-1;
    uintptr_t funcStart = _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);
}
 |