1 /* go-unwind.c -- unwind the stack for panic/recover.
2 
3    Copyright 2010 The Go Authors. All rights reserved.
4    Use of this source code is governed by a BSD-style
5    license that can be found in the LICENSE file.  */
6 
7 #include "config.h"
8 
9 #include <stdlib.h>
10 #include <unistd.h>
11 
12 #include "unwind.h"
13 #define NO_SIZE_OF_ENCODED_VALUE
14 #include "unwind-pe.h"
15 
16 #include "runtime.h"
17 
18 /* The code for a Go exception.  */
19 
20 #ifdef __ARM_EABI_UNWINDER__
21 static const _Unwind_Exception_Class __go_exception_class =
22   { 'G', 'N', 'U', 'C', 'G', 'O', '\0', '\0' };
23 #else
24 static const _Unwind_Exception_Class __go_exception_class =
25   ((((((((_Unwind_Exception_Class) 'G'
26          << 8 | (_Unwind_Exception_Class) 'N')
27         << 8 | (_Unwind_Exception_Class) 'U')
28        << 8 | (_Unwind_Exception_Class) 'C')
29       << 8 | (_Unwind_Exception_Class) 'G')
30      << 8 | (_Unwind_Exception_Class) 'O')
31     << 8 | (_Unwind_Exception_Class) '\0')
32    << 8 | (_Unwind_Exception_Class) '\0');
33 #endif
34 
35 /* Rethrow an exception.  */
36 
37 void rethrowException (void) __asm__(GOSYM_PREFIX "runtime.rethrowException");
38 
39 void
rethrowException()40 rethrowException ()
41 {
42   struct _Unwind_Exception *hdr;
43 
44   hdr = (struct _Unwind_Exception *) runtime_g()->exception;
45 
46 #ifdef __USING_SJLJ_EXCEPTIONS__
47   _Unwind_SjLj_Resume_or_Rethrow (hdr);
48 #else
49 #if defined(_LIBUNWIND_STD_ABI)
50   _Unwind_RaiseException (hdr);
51 #else
52   _Unwind_Resume_or_Rethrow (hdr);
53 #endif
54 #endif
55 
56   /* Rethrowing the exception should not return.  */
57   abort();
58 }
59 
60 /* Return the size of the type that holds an exception header, so that
61    it can be allocated by Go code.  */
62 
63 uintptr unwindExceptionSize(void)
64   __asm__ (GOSYM_PREFIX "runtime.unwindExceptionSize");
65 
66 uintptr
unwindExceptionSize()67 unwindExceptionSize ()
68 {
69   uintptr ret, align;
70 
71   ret = sizeof (struct _Unwind_Exception);
72   /* Adjust the size fo make sure that we can get an aligned value.  */
73   align = __alignof__ (struct _Unwind_Exception);
74   if (align > __alignof__ (uintptr))
75     ret += align - __alignof__ (uintptr);
76   return ret;
77 }
78 
79 /* Throw an exception.  This is called with g->exception pointing to
80    an uninitialized _Unwind_Exception instance.  */
81 
82 void throwException (void) __asm__(GOSYM_PREFIX "runtime.throwException");
83 
84 void
throwException()85 throwException ()
86 {
87   struct _Unwind_Exception *hdr;
88   uintptr align;
89 
90   hdr = (struct _Unwind_Exception *)runtime_g ()->exception;
91 
92   /* Make sure the value is correctly aligned.  It will be large
93      enough, because of unwindExceptionSize.  */
94   align = __alignof__ (struct _Unwind_Exception);
95   hdr = ((struct _Unwind_Exception *)
96 	 (((uintptr) hdr + align - 1) &~ (align - 1)));
97 
98   __builtin_memcpy (&hdr->exception_class, &__go_exception_class,
99 		    sizeof hdr->exception_class);
100   hdr->exception_cleanup = NULL;
101 
102 #ifdef __USING_SJLJ_EXCEPTIONS__
103   _Unwind_SjLj_RaiseException (hdr);
104 #else
105   _Unwind_RaiseException (hdr);
106 #endif
107 
108   /* Raising an exception should not return.  */
109   abort ();
110 }
111 
112 /* The rest of this code is really similar to gcc/unwind-c.c and
113    libjava/exception.cc.  */
114 
115 typedef struct
116 {
117   _Unwind_Ptr Start;
118   _Unwind_Ptr LPStart;
119   _Unwind_Ptr ttype_base;
120   const unsigned char *TType;
121   const unsigned char *action_table;
122   unsigned char ttype_encoding;
123   unsigned char call_site_encoding;
124 } lsda_header_info;
125 
126 static const unsigned char *
parse_lsda_header(struct _Unwind_Context * context,const unsigned char * p,lsda_header_info * info)127 parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
128 		   lsda_header_info *info)
129 {
130   _uleb128_t tmp;
131   unsigned char lpstart_encoding;
132 
133   info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
134 
135   /* Find @LPStart, the base to which landing pad offsets are relative.  */
136   lpstart_encoding = *p++;
137   if (lpstart_encoding != DW_EH_PE_omit)
138     p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart);
139   else
140     info->LPStart = info->Start;
141 
142   /* Find @TType, the base of the handler and exception spec type data.  */
143   info->ttype_encoding = *p++;
144   if (info->ttype_encoding != DW_EH_PE_omit)
145     {
146       p = read_uleb128 (p, &tmp);
147       info->TType = p + tmp;
148     }
149   else
150     info->TType = 0;
151 
152   /* The encoding and length of the call-site table; the action table
153      immediately follows.  */
154   info->call_site_encoding = *p++;
155   p = read_uleb128 (p, &tmp);
156   info->action_table = p + tmp;
157 
158   return p;
159 }
160 
161 /* The personality function is invoked when unwinding the stack due to
162    a panic.  Its job is to find the cleanup and exception handlers to
163    run.  We can't split the stack here, because we won't be able to
164    unwind from that split.  */
165 
166 #ifdef __ARM_EABI_UNWINDER__
167 /* ARM EABI personality routines must also unwind the stack.  */
168 #define CONTINUE_UNWINDING \
169   do								\
170     {								\
171       if (__gnu_unwind_frame (ue_header, context) != _URC_OK)	\
172 	return _URC_FAILURE;					\
173       return _URC_CONTINUE_UNWIND;				\
174     }								\
175   while (0)
176 #else
177 #define CONTINUE_UNWINDING return _URC_CONTINUE_UNWIND
178 #endif
179 
180 #ifdef __USING_SJLJ_EXCEPTIONS__
181 #define PERSONALITY_FUNCTION    __gccgo_personality_sj0
182 #define __builtin_eh_return_data_regno(x) x
183 #else
184 #define PERSONALITY_FUNCTION    __gccgo_personality_v0
185 #endif
186 
187 #ifdef __ARM_EABI_UNWINDER__
188 _Unwind_Reason_Code
189 PERSONALITY_FUNCTION (_Unwind_State, struct _Unwind_Exception *,
190 		      struct _Unwind_Context *)
191   __attribute__ ((no_split_stack, flatten));
192 
193 _Unwind_Reason_Code
PERSONALITY_FUNCTION(_Unwind_State state,struct _Unwind_Exception * ue_header,struct _Unwind_Context * context)194 PERSONALITY_FUNCTION (_Unwind_State state,
195 		      struct _Unwind_Exception * ue_header,
196 		      struct _Unwind_Context * context)
197 #else
198 _Unwind_Reason_Code
199 PERSONALITY_FUNCTION (int, _Unwind_Action, _Unwind_Exception_Class,
200 		      struct _Unwind_Exception *, struct _Unwind_Context *)
201   __attribute__ ((no_split_stack, flatten));
202 
203 _Unwind_Reason_Code
204 PERSONALITY_FUNCTION (int version,
205 		      _Unwind_Action actions,
206 		      _Unwind_Exception_Class exception_class,
207 		      struct _Unwind_Exception *ue_header,
208 		      struct _Unwind_Context *context)
209 #endif
210 {
211   lsda_header_info info;
212   const unsigned char *language_specific_data, *p, *action_record;
213   _Unwind_Ptr landing_pad, ip;
214   int ip_before_insn = 0;
215   _Bool is_foreign;
216   G *g;
217 
218 #ifdef __ARM_EABI_UNWINDER__
219   _Unwind_Action actions;
220 
221   switch (state & _US_ACTION_MASK)
222     {
223     case _US_VIRTUAL_UNWIND_FRAME:
224       actions = _UA_SEARCH_PHASE;
225       break;
226 
227     case _US_UNWIND_FRAME_STARTING:
228       actions = _UA_CLEANUP_PHASE;
229       if (!(state & _US_FORCE_UNWIND)
230 	  && ue_header->barrier_cache.sp == _Unwind_GetGR(context, 13))
231 	actions |= _UA_HANDLER_FRAME;
232       break;
233 
234     case _US_UNWIND_FRAME_RESUME:
235       CONTINUE_UNWINDING;
236       break;
237 
238     default:
239       abort();
240     }
241   actions |= state & _US_FORCE_UNWIND;
242 
243   is_foreign = 0;
244 
245   /* The dwarf unwinder assumes the context structure holds things like the
246      function and LSDA pointers.  The ARM implementation caches these in
247      the exception header (UCB).  To avoid rewriting everything we make the
248      virtual IP register point at the UCB.  */
249   ip = (_Unwind_Ptr) ue_header;
250   _Unwind_SetGR (context, 12, ip);
251 #else
252   if (version != 1)
253     return _URC_FATAL_PHASE1_ERROR;
254 
255   is_foreign = exception_class != __go_exception_class;
256 #endif
257 
258   language_specific_data = (const unsigned char *)
259     _Unwind_GetLanguageSpecificData (context);
260 
261   /* If no LSDA, then there are no handlers or cleanups.  */
262   if (! language_specific_data)
263     CONTINUE_UNWINDING;
264 
265   /* Parse the LSDA header.  */
266   p = parse_lsda_header (context, language_specific_data, &info);
267 #ifdef HAVE_GETIPINFO
268   ip = _Unwind_GetIPInfo (context, &ip_before_insn);
269 #else
270   ip = _Unwind_GetIP (context);
271 #endif
272   if (! ip_before_insn)
273     --ip;
274   landing_pad = 0;
275   action_record = NULL;
276 
277 #ifdef __USING_SJLJ_EXCEPTIONS__
278   /* The given "IP" is an index into the call-site table, with two
279      exceptions -- -1 means no-action, and 0 means terminate.  But
280      since we're using uleb128 values, we've not got random access
281      to the array.  */
282   if ((int) ip <= 0)
283     return _URC_CONTINUE_UNWIND;
284   else
285     {
286       _uleb128_t cs_lp, cs_action;
287       do
288 	{
289 	  p = read_uleb128 (p, &cs_lp);
290 	  p = read_uleb128 (p, &cs_action);
291 	}
292       while (--ip);
293 
294       /* Can never have null landing pad for sjlj -- that would have
295 	 been indicated by a -1 call site index.  */
296       landing_pad = (_Unwind_Ptr)cs_lp + 1;
297       if (cs_action)
298 	action_record = info.action_table + cs_action - 1;
299       goto found_something;
300     }
301 #else
302   /* Search the call-site table for the action associated with this IP.  */
303   while (p < info.action_table)
304     {
305       _Unwind_Ptr cs_start, cs_len, cs_lp;
306       _uleb128_t cs_action;
307 
308       /* Note that all call-site encodings are "absolute" displacements.  */
309       p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
310       p = read_encoded_value (0, info.call_site_encoding, p, &cs_len);
311       p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);
312       p = read_uleb128 (p, &cs_action);
313 
314       /* The table is sorted, so if we've passed the ip, stop.  */
315       if (ip < info.Start + cs_start)
316 	p = info.action_table;
317       else if (ip < info.Start + cs_start + cs_len)
318 	{
319 	  if (cs_lp)
320 	    landing_pad = info.LPStart + cs_lp;
321 	  if (cs_action)
322 	    action_record = info.action_table + cs_action - 1;
323 	  goto found_something;
324 	}
325     }
326 #endif
327 
328   /* IP is not in table.  No associated cleanups.  */
329   CONTINUE_UNWINDING;
330 
331  found_something:
332   if (landing_pad == 0)
333     {
334       /* IP is present, but has a null landing pad.
335 	 No handler to be run.  */
336       CONTINUE_UNWINDING;
337     }
338 
339   if (actions & _UA_SEARCH_PHASE)
340     {
341       if (action_record == 0)
342 	{
343 	  /* This indicates a cleanup rather than an exception
344 	     handler.  */
345 	  CONTINUE_UNWINDING;
346 	}
347 
348       return _URC_HANDLER_FOUND;
349     }
350 
351   /* It's possible for g to be NULL here for an exception thrown by a
352      language other than Go.  */
353   g = runtime_g ();
354   if (g == NULL)
355     {
356       if (!is_foreign)
357 	abort ();
358     }
359   else
360     {
361       g->exception = ue_header;
362       g->isforeign = is_foreign;
363     }
364 
365   _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
366 		 (_Unwind_Ptr) ue_header);
367   _Unwind_SetGR (context, __builtin_eh_return_data_regno (1), 0);
368   _Unwind_SetIP (context, landing_pad);
369   return _URC_INSTALL_CONTEXT;
370 }
371