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