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 #include "go-alloc.h"
18 #include "go-defer.h"
19 #include "go-panic.h"
20 
21 /* The code for a Go exception.  */
22 
23 #ifdef __ARM_EABI_UNWINDER__
24 static const _Unwind_Exception_Class __go_exception_class =
25   { 'G', 'N', 'U', 'C', 'G', 'O', '\0', '\0' };
26 #else
27 static const _Unwind_Exception_Class __go_exception_class =
28   ((((((((_Unwind_Exception_Class) 'G'
29          << 8 | (_Unwind_Exception_Class) 'N')
30         << 8 | (_Unwind_Exception_Class) 'U')
31        << 8 | (_Unwind_Exception_Class) 'C')
32       << 8 | (_Unwind_Exception_Class) 'G')
33      << 8 | (_Unwind_Exception_Class) 'O')
34     << 8 | (_Unwind_Exception_Class) '\0')
35    << 8 | (_Unwind_Exception_Class) '\0');
36 #endif
37 
38 
39 /* This function is called by exception handlers used when unwinding
40    the stack after a recovered panic.  The exception handler looks
41    like this:
42      __go_check_defer (frame);
43      return;
44    If we have not yet reached the frame we are looking for, we
45    continue unwinding.  */
46 
47 void
__go_check_defer(_Bool * frame)48 __go_check_defer (_Bool *frame)
49 {
50   G *g;
51   struct _Unwind_Exception *hdr;
52 
53   g = runtime_g ();
54 
55   if (g == NULL)
56     {
57       /* Some other language has thrown an exception.  We know there
58 	 are no defer handlers, so there is nothing to do.  */
59     }
60   else if (g->is_foreign)
61     {
62       struct __go_panic_stack *n;
63       _Bool was_recovered;
64 
65       /* Some other language has thrown an exception.  We need to run
66 	 the local defer handlers.  If they call recover, we stop
67 	 unwinding the stack here.  */
68 
69       n = ((struct __go_panic_stack *)
70 	   __go_alloc (sizeof (struct __go_panic_stack)));
71 
72       n->__arg.__type_descriptor = NULL;
73       n->__arg.__object = NULL;
74       n->__was_recovered = 0;
75       n->__is_foreign = 1;
76       n->__next = g->panic;
77       g->panic = n;
78 
79       while (1)
80 	{
81 	  struct __go_defer_stack *d;
82 	  void (*pfn) (void *);
83 	  M *m;
84 
85 	  d = g->defer;
86 	  if (d == NULL || d->__frame != frame || d->__pfn == NULL)
87 	    break;
88 
89 	  pfn = d->__pfn;
90 	  g->defer = d->__next;
91 
92 	  (*pfn) (d->__arg);
93 
94 	  m = runtime_m ();
95 	  if (m != NULL && m->mcache != NULL && d->__free)
96 	    __go_free (d);
97 
98 	  if (n->__was_recovered)
99 	    {
100 	      /* The recover function caught the panic thrown by some
101 		 other language.  */
102 	      break;
103 	    }
104 	}
105 
106       was_recovered = n->__was_recovered;
107       g->panic = n->__next;
108       __go_free (n);
109 
110       if (was_recovered)
111 	{
112 	  /* Just return and continue executing Go code.  */
113 	  *frame = 1;
114 	  return;
115 	}
116 
117       /* We are panicing through this function.  */
118       *frame = 0;
119     }
120   else if (g->defer != NULL
121 	   && g->defer->__pfn == NULL
122 	   && g->defer->__frame == frame)
123     {
124       struct __go_defer_stack *d;
125       M *m;
126 
127       /* This is the defer function which called recover.  Simply
128 	 return to stop the stack unwind, and let the Go code continue
129 	 to execute.  */
130       d = g->defer;
131       g->defer = d->__next;
132 
133       m = runtime_m ();
134       if (m != NULL && m->mcache != NULL && d->__free)
135 	__go_free (d);
136 
137       /* We are returning from this function.  */
138       *frame = 1;
139 
140       return;
141     }
142 
143   /* This is some other defer function.  It was already run by the
144      call to panic, or just above.  Rethrow the exception.  */
145 
146   hdr = (struct _Unwind_Exception *) g->exception;
147 
148 #ifdef LIBGO_SJLJ_EXCEPTIONS
149   _Unwind_SjLj_Resume_or_Rethrow (hdr);
150 #else
151 #if defined(_LIBUNWIND_STD_ABI)
152   _Unwind_RaiseException (hdr);
153 #else
154   _Unwind_Resume_or_Rethrow (hdr);
155 #endif
156 #endif
157 
158   /* Rethrowing the exception should not return.  */
159   abort();
160 }
161 
162 /* Unwind function calls until we reach the one which used a defer
163    function which called recover.  Each function which uses a defer
164    statement will have an exception handler, as shown above.  */
165 
166 void
__go_unwind_stack()167 __go_unwind_stack ()
168 {
169   struct _Unwind_Exception *hdr;
170 
171   hdr = ((struct _Unwind_Exception *)
172 	 __go_alloc (sizeof (struct _Unwind_Exception)));
173   __builtin_memcpy (&hdr->exception_class, &__go_exception_class,
174 		    sizeof hdr->exception_class);
175   hdr->exception_cleanup = NULL;
176 
177   runtime_g ()->exception = hdr;
178 
179 #ifdef __USING_SJLJ_EXCEPTIONS__
180   _Unwind_SjLj_RaiseException (hdr);
181 #else
182   _Unwind_RaiseException (hdr);
183 #endif
184 
185   /* Raising an exception should not return.  */
186   abort ();
187 }
188 
189 /* The rest of this code is really similar to gcc/unwind-c.c and
190    libjava/exception.cc.  */
191 
192 typedef struct
193 {
194   _Unwind_Ptr Start;
195   _Unwind_Ptr LPStart;
196   _Unwind_Ptr ttype_base;
197   const unsigned char *TType;
198   const unsigned char *action_table;
199   unsigned char ttype_encoding;
200   unsigned char call_site_encoding;
201 } lsda_header_info;
202 
203 static const unsigned char *
parse_lsda_header(struct _Unwind_Context * context,const unsigned char * p,lsda_header_info * info)204 parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
205 		   lsda_header_info *info)
206 {
207   _uleb128_t tmp;
208   unsigned char lpstart_encoding;
209 
210   info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
211 
212   /* Find @LPStart, the base to which landing pad offsets are relative.  */
213   lpstart_encoding = *p++;
214   if (lpstart_encoding != DW_EH_PE_omit)
215     p = read_encoded_value (context, lpstart_encoding, p, &info->LPStart);
216   else
217     info->LPStart = info->Start;
218 
219   /* Find @TType, the base of the handler and exception spec type data.  */
220   info->ttype_encoding = *p++;
221   if (info->ttype_encoding != DW_EH_PE_omit)
222     {
223       p = read_uleb128 (p, &tmp);
224       info->TType = p + tmp;
225     }
226   else
227     info->TType = 0;
228 
229   /* The encoding and length of the call-site table; the action table
230      immediately follows.  */
231   info->call_site_encoding = *p++;
232   p = read_uleb128 (p, &tmp);
233   info->action_table = p + tmp;
234 
235   return p;
236 }
237 
238 /* The personality function is invoked when unwinding the stack due to
239    a panic.  Its job is to find the cleanup and exception handlers to
240    run.  We can't split the stack here, because we won't be able to
241    unwind from that split.  */
242 
243 #ifdef __ARM_EABI_UNWINDER__
244 /* ARM EABI personality routines must also unwind the stack.  */
245 #define CONTINUE_UNWINDING \
246   do								\
247     {								\
248       if (__gnu_unwind_frame (ue_header, context) != _URC_OK)	\
249 	return _URC_FAILURE;					\
250       return _URC_CONTINUE_UNWIND;				\
251     }								\
252   while (0)
253 #else
254 #define CONTINUE_UNWINDING return _URC_CONTINUE_UNWIND
255 #endif
256 
257 #ifdef __USING_SJLJ_EXCEPTIONS__
258 #define PERSONALITY_FUNCTION    __gccgo_personality_sj0
259 #define __builtin_eh_return_data_regno(x) x
260 #else
261 #define PERSONALITY_FUNCTION    __gccgo_personality_v0
262 #endif
263 
264 #ifdef __ARM_EABI_UNWINDER__
265 _Unwind_Reason_Code
266 PERSONALITY_FUNCTION (_Unwind_State, struct _Unwind_Exception *,
267 		      struct _Unwind_Context *)
268   __attribute__ ((no_split_stack, flatten));
269 
270 _Unwind_Reason_Code
PERSONALITY_FUNCTION(_Unwind_State state,struct _Unwind_Exception * ue_header,struct _Unwind_Context * context)271 PERSONALITY_FUNCTION (_Unwind_State state,
272 		      struct _Unwind_Exception * ue_header,
273 		      struct _Unwind_Context * context)
274 #else
275 _Unwind_Reason_Code
276 PERSONALITY_FUNCTION (int, _Unwind_Action, _Unwind_Exception_Class,
277 		      struct _Unwind_Exception *, struct _Unwind_Context *)
278   __attribute__ ((no_split_stack, flatten));
279 
280 _Unwind_Reason_Code
281 PERSONALITY_FUNCTION (int version,
282 		      _Unwind_Action actions,
283 		      _Unwind_Exception_Class exception_class,
284 		      struct _Unwind_Exception *ue_header,
285 		      struct _Unwind_Context *context)
286 #endif
287 {
288   lsda_header_info info;
289   const unsigned char *language_specific_data, *p, *action_record;
290   _Unwind_Ptr landing_pad, ip;
291   int ip_before_insn = 0;
292   _Bool is_foreign;
293   G *g;
294 
295 #ifdef __ARM_EABI_UNWINDER__
296   _Unwind_Action actions;
297 
298   switch (state & _US_ACTION_MASK)
299     {
300     case _US_VIRTUAL_UNWIND_FRAME:
301       actions = _UA_SEARCH_PHASE;
302       break;
303 
304     case _US_UNWIND_FRAME_STARTING:
305       actions = _UA_CLEANUP_PHASE;
306       if (!(state & _US_FORCE_UNWIND)
307 	  && ue_header->barrier_cache.sp == _Unwind_GetGR(context, 13))
308 	actions |= _UA_HANDLER_FRAME;
309       break;
310 
311     case _US_UNWIND_FRAME_RESUME:
312       CONTINUE_UNWINDING;
313       break;
314 
315     default:
316       abort();
317     }
318   actions |= state & _US_FORCE_UNWIND;
319 
320   is_foreign = 0;
321 
322   /* The dwarf unwinder assumes the context structure holds things like the
323      function and LSDA pointers.  The ARM implementation caches these in
324      the exception header (UCB).  To avoid rewriting everything we make the
325      virtual IP register point at the UCB.  */
326   ip = (_Unwind_Ptr) ue_header;
327   _Unwind_SetGR (context, 12, ip);
328 #else
329   if (version != 1)
330     return _URC_FATAL_PHASE1_ERROR;
331 
332   is_foreign = exception_class != __go_exception_class;
333 #endif
334 
335   language_specific_data = (const unsigned char *)
336     _Unwind_GetLanguageSpecificData (context);
337 
338   /* If no LSDA, then there are no handlers or cleanups.  */
339   if (! language_specific_data)
340     CONTINUE_UNWINDING;
341 
342   /* Parse the LSDA header.  */
343   p = parse_lsda_header (context, language_specific_data, &info);
344 #ifdef HAVE_GETIPINFO
345   ip = _Unwind_GetIPInfo (context, &ip_before_insn);
346 #else
347   ip = _Unwind_GetIP (context);
348 #endif
349   if (! ip_before_insn)
350     --ip;
351   landing_pad = 0;
352   action_record = NULL;
353 
354 #ifdef __USING_SJLJ_EXCEPTIONS__
355   /* The given "IP" is an index into the call-site table, with two
356      exceptions -- -1 means no-action, and 0 means terminate.  But
357      since we're using uleb128 values, we've not got random access
358      to the array.  */
359   if ((int) ip <= 0)
360     return _URC_CONTINUE_UNWIND;
361   else
362     {
363       _uleb128_t cs_lp, cs_action;
364       do
365 	{
366 	  p = read_uleb128 (p, &cs_lp);
367 	  p = read_uleb128 (p, &cs_action);
368 	}
369       while (--ip);
370 
371       /* Can never have null landing pad for sjlj -- that would have
372 	 been indicated by a -1 call site index.  */
373       landing_pad = (_Unwind_Ptr)cs_lp + 1;
374       if (cs_action)
375 	action_record = info.action_table + cs_action - 1;
376       goto found_something;
377     }
378 #else
379   /* Search the call-site table for the action associated with this IP.  */
380   while (p < info.action_table)
381     {
382       _Unwind_Ptr cs_start, cs_len, cs_lp;
383       _uleb128_t cs_action;
384 
385       /* Note that all call-site encodings are "absolute" displacements.  */
386       p = read_encoded_value (0, info.call_site_encoding, p, &cs_start);
387       p = read_encoded_value (0, info.call_site_encoding, p, &cs_len);
388       p = read_encoded_value (0, info.call_site_encoding, p, &cs_lp);
389       p = read_uleb128 (p, &cs_action);
390 
391       /* The table is sorted, so if we've passed the ip, stop.  */
392       if (ip < info.Start + cs_start)
393 	p = info.action_table;
394       else if (ip < info.Start + cs_start + cs_len)
395 	{
396 	  if (cs_lp)
397 	    landing_pad = info.LPStart + cs_lp;
398 	  if (cs_action)
399 	    action_record = info.action_table + cs_action - 1;
400 	  goto found_something;
401 	}
402     }
403 #endif
404 
405   /* IP is not in table.  No associated cleanups.  */
406   CONTINUE_UNWINDING;
407 
408  found_something:
409   if (landing_pad == 0)
410     {
411       /* IP is present, but has a null landing pad.
412 	 No handler to be run.  */
413       CONTINUE_UNWINDING;
414     }
415 
416   if (actions & _UA_SEARCH_PHASE)
417     {
418       if (action_record == 0)
419 	{
420 	  /* This indicates a cleanup rather than an exception
421 	     handler.  */
422 	  CONTINUE_UNWINDING;
423 	}
424 
425       return _URC_HANDLER_FOUND;
426     }
427 
428   /* It's possible for g to be NULL here for an exception thrown by a
429      language other than Go.  */
430   g = runtime_g ();
431   if (g == NULL)
432     {
433       if (!is_foreign)
434 	abort ();
435     }
436   else
437     {
438       g->exception = ue_header;
439       g->is_foreign = is_foreign;
440     }
441 
442   _Unwind_SetGR (context, __builtin_eh_return_data_regno (0),
443 		 (_Unwind_Ptr) ue_header);
444   _Unwind_SetGR (context, __builtin_eh_return_data_regno (1), 0);
445   _Unwind_SetIP (context, landing_pad);
446   return _URC_INSTALL_CONTEXT;
447 }
448