Home | History | Annotate | Download | only in builtins
      1 /* ===-- gcc_personality_v0.c - Implement __gcc_personality_v0 -------------===
      2  *
      3  *      	       The LLVM Compiler Infrastructure
      4  *
      5  * This file is dual licensed under the MIT and the University of Illinois Open
      6  * Source Licenses. See LICENSE.TXT for details.
      7  *
      8  * ===----------------------------------------------------------------------===
      9  *
     10  */
     11 
     12 #include "int_lib.h"
     13 
     14 #include <unwind.h>
     15 
     16 /*
     17  * Pointer encodings documented at:
     18  *   http://refspecs.freestandards.org/LSB_1.3.0/gLSB/gLSB/ehframehdr.html
     19  */
     20 
     21 #define DW_EH_PE_omit      0xff  /* no data follows */
     22 
     23 #define DW_EH_PE_absptr    0x00
     24 #define DW_EH_PE_uleb128   0x01
     25 #define DW_EH_PE_udata2    0x02
     26 #define DW_EH_PE_udata4    0x03
     27 #define DW_EH_PE_udata8    0x04
     28 #define DW_EH_PE_sleb128   0x09
     29 #define DW_EH_PE_sdata2    0x0A
     30 #define DW_EH_PE_sdata4    0x0B
     31 #define DW_EH_PE_sdata8    0x0C
     32 
     33 #define DW_EH_PE_pcrel     0x10
     34 #define DW_EH_PE_textrel   0x20
     35 #define DW_EH_PE_datarel   0x30
     36 #define DW_EH_PE_funcrel   0x40
     37 #define DW_EH_PE_aligned   0x50
     38 #define DW_EH_PE_indirect  0x80 /* gcc extension */
     39 
     40 
     41 
     42 /* read a uleb128 encoded value and advance pointer */
     43 static uintptr_t readULEB128(const uint8_t** data)
     44 {
     45     uintptr_t result = 0;
     46     uintptr_t shift = 0;
     47     unsigned char byte;
     48     const uint8_t* p = *data;
     49     do {
     50         byte = *p++;
     51         result |= (byte & 0x7f) << shift;
     52         shift += 7;
     53     } while (byte & 0x80);
     54     *data = p;
     55     return result;
     56 }
     57 
     58 /* read a pointer encoded value and advance pointer */
     59 static uintptr_t readEncodedPointer(const uint8_t** data, uint8_t encoding)
     60 {
     61     const uint8_t* p = *data;
     62     uintptr_t result = 0;
     63 
     64     if ( encoding == DW_EH_PE_omit )
     65         return 0;
     66 
     67     /* first get value */
     68     switch (encoding & 0x0F) {
     69         case DW_EH_PE_absptr:
     70             result = *((const uintptr_t*)p);
     71             p += sizeof(uintptr_t);
     72             break;
     73         case DW_EH_PE_uleb128:
     74             result = readULEB128(&p);
     75             break;
     76         case DW_EH_PE_udata2:
     77             result = *((const uint16_t*)p);
     78             p += sizeof(uint16_t);
     79             break;
     80         case DW_EH_PE_udata4:
     81             result = *((const uint32_t*)p);
     82             p += sizeof(uint32_t);
     83             break;
     84         case DW_EH_PE_udata8:
     85             result = *((const uint64_t*)p);
     86             p += sizeof(uint64_t);
     87             break;
     88         case DW_EH_PE_sdata2:
     89             result = *((const int16_t*)p);
     90             p += sizeof(int16_t);
     91             break;
     92         case DW_EH_PE_sdata4:
     93             result = *((const int32_t*)p);
     94             p += sizeof(int32_t);
     95             break;
     96         case DW_EH_PE_sdata8:
     97             result = *((const int64_t*)p);
     98             p += sizeof(int64_t);
     99             break;
    100         case DW_EH_PE_sleb128:
    101         default:
    102             /* not supported */
    103             compilerrt_abort();
    104             break;
    105     }
    106 
    107     /* then add relative offset */
    108     switch ( encoding & 0x70 ) {
    109         case DW_EH_PE_absptr:
    110             /* do nothing */
    111             break;
    112         case DW_EH_PE_pcrel:
    113             result += (uintptr_t)(*data);
    114             break;
    115         case DW_EH_PE_textrel:
    116         case DW_EH_PE_datarel:
    117         case DW_EH_PE_funcrel:
    118         case DW_EH_PE_aligned:
    119         default:
    120             /* not supported */
    121             compilerrt_abort();
    122             break;
    123     }
    124 
    125     /* then apply indirection */
    126     if (encoding & DW_EH_PE_indirect) {
    127         result = *((const uintptr_t*)result);
    128     }
    129 
    130     *data = p;
    131     return result;
    132 }
    133 
    134 #if defined(__arm__) && !defined(__USING_SJLJ_EXCEPTIONS__) &&                 \
    135     !defined(__ARM_DWARF_EH__)
    136 #define USING_ARM_EHABI 1
    137 _Unwind_Reason_Code __gnu_unwind_frame(struct _Unwind_Exception *,
    138                                        struct _Unwind_Context *);
    139 #endif
    140 
    141 static inline _Unwind_Reason_Code
    142 continueUnwind(struct _Unwind_Exception *exceptionObject,
    143                struct _Unwind_Context *context) {
    144 #if USING_ARM_EHABI
    145     /*
    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      */
    149     if (__gnu_unwind_frame(exceptionObject, context) != _URC_OK)
    150         return _URC_FAILURE;
    151 #endif
    152     return _URC_CONTINUE_UNWIND;
    153 }
    154 
    155 /*
    156  * The C compiler makes references to __gcc_personality_v0 in
    157  * the dwarf unwind information for translation units that use
    158  * __attribute__((cleanup(xx))) on local variables.
    159  * This personality routine is called by the system unwinder
    160  * on each frame as the stack is unwound during a C++ exception
    161  * throw through a C function compiled with -fexceptions.
    162  */
    163 #if __USING_SJLJ_EXCEPTIONS__
    164 /* the setjump-longjump based exceptions personality routine has a
    165  * different name */
    166 COMPILER_RT_ABI _Unwind_Reason_Code
    167 __gcc_personality_sj0(int version, _Unwind_Action actions,
    168          uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject,
    169          struct _Unwind_Context *context)
    170 #elif USING_ARM_EHABI
    171 /* The ARM EHABI personality routine has a different signature. */
    172 COMPILER_RT_ABI _Unwind_Reason_Code __gcc_personality_v0(
    173          _Unwind_State state, struct _Unwind_Exception *exceptionObject,
    174          struct _Unwind_Context *context)
    175 #else
    176 COMPILER_RT_ABI _Unwind_Reason_Code
    177 __gcc_personality_v0(int version, _Unwind_Action actions,
    178          uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject,
    179          struct _Unwind_Context *context)
    180 #endif
    181 {
    182     /* Since C does not have catch clauses, there is nothing to do during */
    183     /* phase 1 (the search phase). */
    184 #if USING_ARM_EHABI
    185     /* After resuming from a cleanup we should also continue on to the next
    186      * frame straight away. */
    187     if ((state & _US_ACTION_MASK) != _US_UNWIND_FRAME_STARTING)
    188 #else
    189     if ( actions & _UA_SEARCH_PHASE )
    190 #endif
    191         return continueUnwind(exceptionObject, context);
    192 
    193     /* There is nothing to do if there is no LSDA for this frame. */
    194     const uint8_t* lsda = (uint8_t*)_Unwind_GetLanguageSpecificData(context);
    195     if ( lsda == (uint8_t*) 0 )
    196         return continueUnwind(exceptionObject, context);
    197 
    198     uintptr_t pc = _Unwind_GetIP(context)-1;
    199     uintptr_t funcStart = _Unwind_GetRegionStart(context);
    200     uintptr_t pcOffset = pc - funcStart;
    201 
    202     /* Parse LSDA header. */
    203     uint8_t lpStartEncoding = *lsda++;
    204     if (lpStartEncoding != DW_EH_PE_omit) {
    205         readEncodedPointer(&lsda, lpStartEncoding);
    206     }
    207     uint8_t ttypeEncoding = *lsda++;
    208     if (ttypeEncoding != DW_EH_PE_omit) {
    209         readULEB128(&lsda);
    210     }
    211     /* Walk call-site table looking for range that includes current PC. */
    212     uint8_t         callSiteEncoding = *lsda++;
    213     uint32_t        callSiteTableLength = readULEB128(&lsda);
    214     const uint8_t*  callSiteTableStart = lsda;
    215     const uint8_t*  callSiteTableEnd = callSiteTableStart + callSiteTableLength;
    216     const uint8_t* p=callSiteTableStart;
    217     while (p < callSiteTableEnd) {
    218         uintptr_t start = readEncodedPointer(&p, callSiteEncoding);
    219         uintptr_t length = readEncodedPointer(&p, callSiteEncoding);
    220         uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding);
    221         readULEB128(&p); /* action value not used for C code */
    222         if ( landingPad == 0 )
    223             continue; /* no landing pad for this entry */
    224         if ( (start <= pcOffset) && (pcOffset < (start+length)) ) {
    225             /* Found landing pad for the PC.
    226              * Set Instruction Pointer to so we re-enter function
    227              * at landing pad. The landing pad is created by the compiler
    228              * to take two parameters in registers.
    229              */
    230             _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
    231                           (uintptr_t)exceptionObject);
    232             _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
    233             _Unwind_SetIP(context, (funcStart + landingPad));
    234             return _URC_INSTALL_CONTEXT;
    235         }
    236     }
    237 
    238     /* No landing pad found, continue unwinding. */
    239     return continueUnwind(exceptionObject, context);
    240 }
    241 
    242