[gcc/testsuite]
[official-gcc.git] / libgo / runtime / go-unwind.c
blob4c9fb49c9942e5ff3c0aa394585a25e4ca59c024
1 /* go-unwind.c -- unwind the stack for panic/recover.
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. */
7 #include "config.h"
9 #include <stdlib.h>
10 #include <unistd.h>
12 #include "unwind.h"
13 #define NO_SIZE_OF_ENCODED_VALUE
14 #include "unwind-pe.h"
16 #include "runtime.h"
18 /* The code for a Go exception. */
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
35 /* Rethrow an exception. */
37 void rethrowException (void) __asm__(GOSYM_PREFIX "runtime.rethrowException");
39 void
40 rethrowException ()
42 struct _Unwind_Exception *hdr;
44 hdr = (struct _Unwind_Exception *) runtime_g()->exception;
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
56 /* Rethrowing the exception should not return. */
57 abort();
60 /* Return the size of the type that holds an exception header, so that
61 it can be allocated by Go code. */
63 uintptr unwindExceptionSize(void)
64 __asm__ (GOSYM_PREFIX "runtime.unwindExceptionSize");
66 uintptr
67 unwindExceptionSize ()
69 uintptr ret, align;
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;
79 /* Throw an exception. This is called with g->exception pointing to
80 an uninitialized _Unwind_Exception instance. */
82 void throwException (void) __asm__(GOSYM_PREFIX "runtime.throwException");
84 void
85 throwException ()
87 struct _Unwind_Exception *hdr;
88 uintptr align;
90 hdr = (struct _Unwind_Exception *)runtime_g ()->exception;
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)));
98 __builtin_memcpy (&hdr->exception_class, &__go_exception_class,
99 sizeof hdr->exception_class);
100 hdr->exception_cleanup = NULL;
102 #ifdef __USING_SJLJ_EXCEPTIONS__
103 _Unwind_SjLj_RaiseException (hdr);
104 #else
105 _Unwind_RaiseException (hdr);
106 #endif
108 /* Raising an exception should not return. */
109 abort ();
112 /* The rest of this code is really similar to gcc/unwind-c.c and
113 libjava/exception.cc. */
115 typedef struct
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;
126 static const unsigned char *
127 parse_lsda_header (struct _Unwind_Context *context, const unsigned char *p,
128 lsda_header_info *info)
130 _uleb128_t tmp;
131 unsigned char lpstart_encoding;
133 info->Start = (context ? _Unwind_GetRegionStart (context) : 0);
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;
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)
146 p = read_uleb128 (p, &tmp);
147 info->TType = p + tmp;
149 else
150 info->TType = 0;
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;
158 return p;
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. */
166 #ifdef __ARM_EABI_UNWINDER__
167 /* ARM EABI personality routines must also unwind the stack. */
168 #define CONTINUE_UNWINDING \
169 do \
171 if (__gnu_unwind_frame (ue_header, context) != _URC_OK) \
172 return _URC_FAILURE; \
173 return _URC_CONTINUE_UNWIND; \
175 while (0)
176 #else
177 #define CONTINUE_UNWINDING return _URC_CONTINUE_UNWIND
178 #endif
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
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));
193 _Unwind_Reason_Code
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));
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
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;
218 #ifdef __ARM_EABI_UNWINDER__
219 _Unwind_Action actions;
221 switch (state & _US_ACTION_MASK)
223 case _US_VIRTUAL_UNWIND_FRAME:
224 actions = _UA_SEARCH_PHASE;
225 break;
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;
234 case _US_UNWIND_FRAME_RESUME:
235 CONTINUE_UNWINDING;
236 break;
238 default:
239 abort();
241 actions |= state & _US_FORCE_UNWIND;
243 is_foreign = 0;
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;
255 is_foreign = exception_class != __go_exception_class;
256 #endif
258 language_specific_data = (const unsigned char *)
259 _Unwind_GetLanguageSpecificData (context);
261 /* If no LSDA, then there are no handlers or cleanups. */
262 if (! language_specific_data)
263 CONTINUE_UNWINDING;
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;
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
286 _uleb128_t cs_lp, cs_action;
289 p = read_uleb128 (p, &cs_lp);
290 p = read_uleb128 (p, &cs_action);
292 while (--ip);
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;
301 #else
302 /* Search the call-site table for the action associated with this IP. */
303 while (p < info.action_table)
305 _Unwind_Ptr cs_start, cs_len, cs_lp;
306 _uleb128_t cs_action;
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);
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)
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;
326 #endif
328 /* IP is not in table. No associated cleanups. */
329 CONTINUE_UNWINDING;
331 found_something:
332 if (landing_pad == 0)
334 /* IP is present, but has a null landing pad.
335 No handler to be run. */
336 CONTINUE_UNWINDING;
339 if (actions & _UA_SEARCH_PHASE)
341 if (action_record == 0)
343 /* This indicates a cleanup rather than an exception
344 handler. */
345 CONTINUE_UNWINDING;
348 return _URC_HANDLER_FOUND;
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)
356 if (!is_foreign)
357 abort ();
359 else
361 g->exception = ue_header;
362 g->isforeign = is_foreign;
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;