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