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