comparison gcc/config/xtensa/unwind-dw2-xtensa.c @ 0:a06113de4d67

first commit
author kent <kent@cr.ie.u-ryukyu.ac.jp>
date Fri, 17 Jul 2009 14:47:48 +0900
parents
children 77e2b8dfacca
comparison
equal deleted inserted replaced
-1:000000000000 0:a06113de4d67
1 /* DWARF2 exception handling and frame unwinding for Xtensa.
2 Copyright (C) 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
3 2007, 2008, 2009
4 Free Software Foundation, Inc.
5
6 This file is part of GCC.
7
8 GCC is free software; you can redistribute it and/or modify it
9 under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 3, or (at your option)
11 any later version.
12
13 GCC is distributed in the hope that it will be useful, but WITHOUT
14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
15 or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
16 License for more details.
17
18 Under Section 7 of GPL version 3, you are granted additional
19 permissions described in the GCC Runtime Library Exception, version
20 3.1, as published by the Free Software Foundation.
21
22 You should have received a copy of the GNU General Public License and
23 a copy of the GCC Runtime Library Exception along with this program;
24 see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
25 <http://www.gnu.org/licenses/>. */
26
27 #include "tconfig.h"
28 #include "tsystem.h"
29 #include "coretypes.h"
30 #include "tm.h"
31 #include "dwarf2.h"
32 #include "unwind.h"
33 #ifdef __USING_SJLJ_EXCEPTIONS__
34 # define NO_SIZE_OF_ENCODED_VALUE
35 #endif
36 #include "unwind-pe.h"
37 #include "unwind-dw2-fde.h"
38 #include "unwind-dw2-xtensa.h"
39
40 #ifndef __USING_SJLJ_EXCEPTIONS__
41
42 /* The standard CIE and FDE structures work fine for Xtensa but the
43 variable-size register window save areas are not a good fit for the rest
44 of the standard DWARF unwinding mechanism. Nor is that mechanism
45 necessary, since the register save areas are always in fixed locations
46 in each stack frame. This file is a stripped down and customized version
47 of the standard DWARF unwinding code. It needs to be customized to have
48 builtin logic for finding the save areas and also to track the stack
49 pointer value (besides the CFA) while unwinding since the primary save
50 area is located below the stack pointer. It is stripped down to reduce
51 code size and ease the maintenance burden of tracking changes in the
52 standard version of the code. */
53
54 #ifndef DWARF_REG_TO_UNWIND_COLUMN
55 #define DWARF_REG_TO_UNWIND_COLUMN(REGNO) (REGNO)
56 #endif
57
58 #define XTENSA_RA_FIELD_MASK 0x3FFFFFFF
59
60 /* This is the register and unwind state for a particular frame. This
61 provides the information necessary to unwind up past a frame and return
62 to its caller. */
63 struct _Unwind_Context
64 {
65 /* Track register window save areas of 4 registers each, instead of
66 keeping separate addresses for the individual registers. */
67 _Unwind_Word *reg[4];
68
69 void *cfa;
70 void *sp;
71 void *ra;
72
73 /* Cache the 2 high bits to replace the window size in return addresses. */
74 _Unwind_Word ra_high_bits;
75
76 void *lsda;
77 struct dwarf_eh_bases bases;
78 /* Signal frame context. */
79 #define SIGNAL_FRAME_BIT ((~(_Unwind_Word) 0 >> 1) + 1)
80 _Unwind_Word flags;
81 /* 0 for now, can be increased when further fields are added to
82 struct _Unwind_Context. */
83 _Unwind_Word version;
84 };
85
86
87 /* Read unaligned data from the instruction buffer. */
88
89 union unaligned
90 {
91 void *p;
92 } __attribute__ ((packed));
93
94 static void uw_update_context (struct _Unwind_Context *, _Unwind_FrameState *);
95 static _Unwind_Reason_Code uw_frame_state_for (struct _Unwind_Context *,
96 _Unwind_FrameState *);
97
98 static inline void *
99 read_pointer (const void *p) { const union unaligned *up = p; return up->p; }
100
101 static inline _Unwind_Word
102 _Unwind_IsSignalFrame (struct _Unwind_Context *context)
103 {
104 return (context->flags & SIGNAL_FRAME_BIT) ? 1 : 0;
105 }
106
107 static inline void
108 _Unwind_SetSignalFrame (struct _Unwind_Context *context, int val)
109 {
110 if (val)
111 context->flags |= SIGNAL_FRAME_BIT;
112 else
113 context->flags &= ~SIGNAL_FRAME_BIT;
114 }
115
116 /* Get the value of register INDEX as saved in CONTEXT. */
117
118 inline _Unwind_Word
119 _Unwind_GetGR (struct _Unwind_Context *context, int index)
120 {
121 _Unwind_Word *ptr;
122
123 index = DWARF_REG_TO_UNWIND_COLUMN (index);
124 ptr = context->reg[index >> 2] + (index & 3);
125
126 return *ptr;
127 }
128
129 /* Get the value of the CFA as saved in CONTEXT. */
130
131 _Unwind_Word
132 _Unwind_GetCFA (struct _Unwind_Context *context)
133 {
134 return (_Unwind_Ptr) context->cfa;
135 }
136
137 /* Overwrite the saved value for register INDEX in CONTEXT with VAL. */
138
139 inline void
140 _Unwind_SetGR (struct _Unwind_Context *context, int index, _Unwind_Word val)
141 {
142 _Unwind_Word *ptr;
143
144 index = DWARF_REG_TO_UNWIND_COLUMN (index);
145 ptr = context->reg[index >> 2] + (index & 3);
146
147 *ptr = val;
148 }
149
150 /* Retrieve the return address for CONTEXT. */
151
152 inline _Unwind_Ptr
153 _Unwind_GetIP (struct _Unwind_Context *context)
154 {
155 return (_Unwind_Ptr) context->ra;
156 }
157
158 /* Retrieve the return address and flag whether that IP is before
159 or after first not yet fully executed instruction. */
160
161 inline _Unwind_Ptr
162 _Unwind_GetIPInfo (struct _Unwind_Context *context, int *ip_before_insn)
163 {
164 *ip_before_insn = _Unwind_IsSignalFrame (context);
165 return (_Unwind_Ptr) context->ra;
166 }
167
168 /* Overwrite the return address for CONTEXT with VAL. */
169
170 inline void
171 _Unwind_SetIP (struct _Unwind_Context *context, _Unwind_Ptr val)
172 {
173 context->ra = (void *) val;
174 }
175
176 void *
177 _Unwind_GetLanguageSpecificData (struct _Unwind_Context *context)
178 {
179 return context->lsda;
180 }
181
182 _Unwind_Ptr
183 _Unwind_GetRegionStart (struct _Unwind_Context *context)
184 {
185 return (_Unwind_Ptr) context->bases.func;
186 }
187
188 void *
189 _Unwind_FindEnclosingFunction (void *pc)
190 {
191 struct dwarf_eh_bases bases;
192 const struct dwarf_fde *fde = _Unwind_Find_FDE (pc-1, &bases);
193 if (fde)
194 return bases.func;
195 else
196 return NULL;
197 }
198
199 _Unwind_Ptr
200 _Unwind_GetDataRelBase (struct _Unwind_Context *context)
201 {
202 return (_Unwind_Ptr) context->bases.dbase;
203 }
204
205 _Unwind_Ptr
206 _Unwind_GetTextRelBase (struct _Unwind_Context *context)
207 {
208 return (_Unwind_Ptr) context->bases.tbase;
209 }
210
211 #ifdef MD_UNWIND_SUPPORT
212 #include MD_UNWIND_SUPPORT
213 #endif
214
215 /* Extract any interesting information from the CIE for the translation
216 unit F belongs to. Return a pointer to the byte after the augmentation,
217 or NULL if we encountered an undecipherable augmentation. */
218
219 static const unsigned char *
220 extract_cie_info (const struct dwarf_cie *cie, struct _Unwind_Context *context,
221 _Unwind_FrameState *fs)
222 {
223 const unsigned char *aug = cie->augmentation;
224 const unsigned char *p = aug + strlen ((const char *)aug) + 1;
225 const unsigned char *ret = NULL;
226 _uleb128_t utmp;
227 _sleb128_t stmp;
228
229 /* g++ v2 "eh" has pointer immediately following augmentation string,
230 so it must be handled first. */
231 if (aug[0] == 'e' && aug[1] == 'h')
232 {
233 fs->eh_ptr = read_pointer (p);
234 p += sizeof (void *);
235 aug += 2;
236 }
237
238 /* Immediately following the augmentation are the code and
239 data alignment and return address column. */
240 p = read_uleb128 (p, &utmp);
241 p = read_sleb128 (p, &stmp);
242 if (cie->version == 1)
243 fs->retaddr_column = *p++;
244 else
245 {
246 p = read_uleb128 (p, &utmp);
247 fs->retaddr_column = (_Unwind_Word)utmp;
248 }
249 fs->lsda_encoding = DW_EH_PE_omit;
250
251 /* If the augmentation starts with 'z', then a uleb128 immediately
252 follows containing the length of the augmentation field following
253 the size. */
254 if (*aug == 'z')
255 {
256 p = read_uleb128 (p, &utmp);
257 ret = p + utmp;
258
259 fs->saw_z = 1;
260 ++aug;
261 }
262
263 /* Iterate over recognized augmentation subsequences. */
264 while (*aug != '\0')
265 {
266 /* "L" indicates a byte showing how the LSDA pointer is encoded. */
267 if (aug[0] == 'L')
268 {
269 fs->lsda_encoding = *p++;
270 aug += 1;
271 }
272
273 /* "R" indicates a byte indicating how FDE addresses are encoded. */
274 else if (aug[0] == 'R')
275 {
276 fs->fde_encoding = *p++;
277 aug += 1;
278 }
279
280 /* "P" indicates a personality routine in the CIE augmentation. */
281 else if (aug[0] == 'P')
282 {
283 _Unwind_Ptr personality;
284
285 p = read_encoded_value (context, *p, p + 1, &personality);
286 fs->personality = (_Unwind_Personality_Fn) personality;
287 aug += 1;
288 }
289
290 /* "S" indicates a signal frame. */
291 else if (aug[0] == 'S')
292 {
293 fs->signal_frame = 1;
294 aug += 1;
295 }
296
297 /* Otherwise we have an unknown augmentation string.
298 Bail unless we saw a 'z' prefix. */
299 else
300 return ret;
301 }
302
303 return ret ? ret : p;
304 }
305
306 /* Given the _Unwind_Context CONTEXT for a stack frame, look up the FDE for
307 its caller and decode it into FS. This function also sets the
308 lsda member of CONTEXT, as it is really information
309 about the caller's frame. */
310
311 static _Unwind_Reason_Code
312 uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs)
313 {
314 const struct dwarf_fde *fde;
315 const struct dwarf_cie *cie;
316 const unsigned char *aug;
317 int window_size;
318 _Unwind_Word *ra_ptr;
319
320 memset (fs, 0, sizeof (*fs));
321 context->lsda = 0;
322
323 fde = _Unwind_Find_FDE (context->ra + _Unwind_IsSignalFrame (context) - 1,
324 &context->bases);
325 if (fde == NULL)
326 {
327 #ifdef MD_FALLBACK_FRAME_STATE_FOR
328 _Unwind_Reason_Code reason;
329 /* Couldn't find frame unwind info for this function. Try a
330 target-specific fallback mechanism. This will necessarily
331 not provide a personality routine or LSDA. */
332 reason = MD_FALLBACK_FRAME_STATE_FOR (context, fs);
333 if (reason != _URC_END_OF_STACK)
334 return reason;
335 #endif
336 /* The frame was not recognized and handled by the fallback function,
337 but it is not really the end of the stack. Fall through here and
338 unwind it anyway. */
339 }
340 else
341 {
342 cie = get_cie (fde);
343 if (extract_cie_info (cie, context, fs) == NULL)
344 /* CIE contained unknown augmentation. */
345 return _URC_FATAL_PHASE1_ERROR;
346
347 /* Locate augmentation for the fde. */
348 aug = (const unsigned char *) fde + sizeof (*fde);
349 aug += 2 * size_of_encoded_value (fs->fde_encoding);
350 if (fs->saw_z)
351 {
352 _uleb128_t i;
353 aug = read_uleb128 (aug, &i);
354 }
355 if (fs->lsda_encoding != DW_EH_PE_omit)
356 {
357 _Unwind_Ptr lsda;
358
359 aug = read_encoded_value (context, fs->lsda_encoding, aug, &lsda);
360 context->lsda = (void *) lsda;
361 }
362 }
363
364 /* Check for the end of the stack. This needs to be checked after
365 the MD_FALLBACK_FRAME_STATE_FOR check for signal frames because
366 the contents of context->reg[0] are undefined at a signal frame,
367 and register a0 may appear to be zero. (The return address in
368 context->ra comes from register a4 or a8). */
369 ra_ptr = context->reg[0];
370 if (ra_ptr && *ra_ptr == 0)
371 return _URC_END_OF_STACK;
372
373 /* Find the window size from the high bits of the return address. */
374 if (ra_ptr)
375 window_size = (*ra_ptr >> 30) * 4;
376 else
377 window_size = 8;
378
379 fs->retaddr_column = window_size;
380
381 return _URC_NO_REASON;
382 }
383
384 static void
385 uw_update_context_1 (struct _Unwind_Context *context, _Unwind_FrameState *fs)
386 {
387 struct _Unwind_Context orig_context = *context;
388 _Unwind_Word *sp, *cfa, *next_cfa;
389 int i;
390
391 if (fs->signal_regs)
392 {
393 cfa = (_Unwind_Word *) fs->signal_regs[1];
394 next_cfa = (_Unwind_Word *) cfa[-3];
395
396 for (i = 0; i < 4; i++)
397 context->reg[i] = fs->signal_regs + (i << 2);
398 }
399 else
400 {
401 int window_size = fs->retaddr_column >> 2;
402
403 sp = (_Unwind_Word *) orig_context.sp;
404 cfa = (_Unwind_Word *) orig_context.cfa;
405 next_cfa = (_Unwind_Word *) cfa[-3];
406
407 /* Registers a0-a3 are in the save area below sp. */
408 context->reg[0] = sp - 4;
409
410 /* Find the extra save area below next_cfa. */
411 for (i = 1; i < window_size; i++)
412 context->reg[i] = next_cfa - 4 * (1 + window_size - i);
413
414 /* Remaining registers rotate from previous save areas. */
415 for (i = window_size; i < 4; i++)
416 context->reg[i] = orig_context.reg[i - window_size];
417 }
418
419 context->sp = cfa;
420 context->cfa = next_cfa;
421
422 _Unwind_SetSignalFrame (context, fs->signal_frame);
423 }
424
425 /* CONTEXT describes the unwind state for a frame, and FS describes the FDE
426 of its caller. Update CONTEXT to refer to the caller as well. Note
427 that the lsda member is not updated here, but later in
428 uw_frame_state_for. */
429
430 static void
431 uw_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs)
432 {
433 uw_update_context_1 (context, fs);
434
435 /* Compute the return address now, since the return address column
436 can change from frame to frame. */
437 if (fs->signal_ra != 0)
438 context->ra = (void *) fs->signal_ra;
439 else
440 context->ra = (void *) ((_Unwind_GetGR (context, fs->retaddr_column)
441 & XTENSA_RA_FIELD_MASK) | context->ra_high_bits);
442 }
443
444 static void
445 uw_advance_context (struct _Unwind_Context *context, _Unwind_FrameState *fs)
446 {
447 uw_update_context (context, fs);
448 }
449
450 /* Fill in CONTEXT for top-of-stack. The only valid registers at this
451 level will be the return address and the CFA. */
452
453 #define uw_init_context(CONTEXT) \
454 do \
455 { \
456 __builtin_unwind_init (); \
457 uw_init_context_1 (CONTEXT, __builtin_dwarf_cfa (), \
458 __builtin_return_address (0)); \
459 } \
460 while (0)
461
462 static void
463 uw_init_context_1 (struct _Unwind_Context *context, void *outer_cfa,
464 void *outer_ra)
465 {
466 void *ra = __builtin_return_address (0);
467 void *cfa = __builtin_dwarf_cfa ();
468 _Unwind_FrameState fs;
469
470 memset (context, 0, sizeof (struct _Unwind_Context));
471 context->ra = ra;
472
473 memset (&fs, 0, sizeof (fs));
474 fs.retaddr_column = 8;
475 context->sp = cfa;
476 context->cfa = outer_cfa;
477 context->ra_high_bits =
478 ((_Unwind_Word) uw_init_context_1) & ~XTENSA_RA_FIELD_MASK;
479 uw_update_context_1 (context, &fs);
480
481 context->ra = outer_ra;
482 }
483
484
485 /* Install TARGET into CURRENT so that we can return to it. This is a
486 macro because __builtin_eh_return must be invoked in the context of
487 our caller. */
488
489 #define uw_install_context(CURRENT, TARGET) \
490 do \
491 { \
492 long offset = uw_install_context_1 ((CURRENT), (TARGET)); \
493 void *handler = __builtin_frob_return_addr ((TARGET)->ra); \
494 __builtin_eh_return (offset, handler); \
495 } \
496 while (0)
497
498 static long
499 uw_install_context_1 (struct _Unwind_Context *current,
500 struct _Unwind_Context *target)
501 {
502 long i;
503
504 /* The eh_return insn assumes a window size of 8, so don't bother copying
505 the save areas for registers a8-a15 since they won't be reloaded. */
506 for (i = 0; i < 2; ++i)
507 {
508 void *c = current->reg[i];
509 void *t = target->reg[i];
510
511 if (t && c && t != c)
512 memcpy (c, t, 4 * sizeof (_Unwind_Word));
513 }
514
515 return 0;
516 }
517
518 static inline _Unwind_Ptr
519 uw_identify_context (struct _Unwind_Context *context)
520 {
521 return _Unwind_GetCFA (context);
522 }
523
524
525 #include "unwind.inc"
526
527 #if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS)
528 alias (_Unwind_Backtrace);
529 alias (_Unwind_DeleteException);
530 alias (_Unwind_FindEnclosingFunction);
531 alias (_Unwind_ForcedUnwind);
532 alias (_Unwind_GetDataRelBase);
533 alias (_Unwind_GetTextRelBase);
534 alias (_Unwind_GetCFA);
535 alias (_Unwind_GetGR);
536 alias (_Unwind_GetIP);
537 alias (_Unwind_GetLanguageSpecificData);
538 alias (_Unwind_GetRegionStart);
539 alias (_Unwind_RaiseException);
540 alias (_Unwind_Resume);
541 alias (_Unwind_Resume_or_Rethrow);
542 alias (_Unwind_SetGR);
543 alias (_Unwind_SetIP);
544 #endif
545
546 #endif /* !USING_SJLJ_EXCEPTIONS */