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