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 
    135 /*
    136  * The C compiler makes references to __gcc_personality_v0 in
    137  * the dwarf unwind information for translation units that use
    138  * __attribute__((cleanup(xx))) on local variables.
    139  * This personality routine is called by the system unwinder
    140  * on each frame as the stack is unwound during a C++ exception
    141  * throw through a C function compiled with -fexceptions.
    142  */
    143 #if __USING_SJLJ_EXCEPTIONS__
    144 // the setjump-longjump based exceptions personality routine has a different name
    145 COMPILER_RT_ABI _Unwind_Reason_Code
    146 __gcc_personality_sj0(int version, _Unwind_Action actions,
    147          uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject,
    148          struct _Unwind_Context *context)
    149 #else
    150 COMPILER_RT_ABI _Unwind_Reason_Code
    151 __gcc_personality_v0(int version, _Unwind_Action actions,
    152          uint64_t exceptionClass, struct _Unwind_Exception* exceptionObject,
    153          struct _Unwind_Context *context)
    154 #endif
    155 {
    156     /* Since C does not have catch clauses, there is nothing to do during */
    157     /* phase 1 (the search phase). */
    158     if ( actions & _UA_SEARCH_PHASE )
    159         return _URC_CONTINUE_UNWIND;
    160 
    161     /* There is nothing to do if there is no LSDA for this frame. */
    162     const uint8_t* lsda = (uint8_t*)_Unwind_GetLanguageSpecificData(context);
    163     if ( lsda == (uint8_t*) 0 )
    164         return _URC_CONTINUE_UNWIND;
    165 
    166     uintptr_t pc = _Unwind_GetIP(context)-1;
    167     uintptr_t funcStart = _Unwind_GetRegionStart(context);
    168     uintptr_t pcOffset = pc - funcStart;
    169 
    170     /* Parse LSDA header. */
    171     uint8_t lpStartEncoding = *lsda++;
    172     if (lpStartEncoding != DW_EH_PE_omit) {
    173         readEncodedPointer(&lsda, lpStartEncoding);
    174     }
    175     uint8_t ttypeEncoding = *lsda++;
    176     if (ttypeEncoding != DW_EH_PE_omit) {
    177         readULEB128(&lsda);
    178     }
    179     /* Walk call-site table looking for range that includes current PC. */
    180     uint8_t         callSiteEncoding = *lsda++;
    181     uint32_t        callSiteTableLength = readULEB128(&lsda);
    182     const uint8_t*  callSiteTableStart = lsda;
    183     const uint8_t*  callSiteTableEnd = callSiteTableStart + callSiteTableLength;
    184     const uint8_t* p=callSiteTableStart;
    185     while (p < callSiteTableEnd) {
    186         uintptr_t start = readEncodedPointer(&p, callSiteEncoding);
    187         uintptr_t length = readEncodedPointer(&p, callSiteEncoding);
    188         uintptr_t landingPad = readEncodedPointer(&p, callSiteEncoding);
    189         readULEB128(&p); /* action value not used for C code */
    190         if ( landingPad == 0 )
    191             continue; /* no landing pad for this entry */
    192         if ( (start <= pcOffset) && (pcOffset < (start+length)) ) {
    193             /* Found landing pad for the PC.
    194              * Set Instruction Pointer to so we re-enter function
    195              * at landing pad. The landing pad is created by the compiler
    196              * to take two parameters in registers.
    197              */
    198             _Unwind_SetGR(context, __builtin_eh_return_data_regno(0),
    199                           (uintptr_t)exceptionObject);
    200             _Unwind_SetGR(context, __builtin_eh_return_data_regno(1), 0);
    201             _Unwind_SetIP(context, (funcStart + landingPad));
    202             return _URC_INSTALL_CONTEXT;
    203         }
    204     }
    205 
    206     /* No landing pad found, continue unwinding. */
    207     return _URC_CONTINUE_UNWIND;
    208 }
    209 
    210