summaryrefslogtreecommitdiff
path: root/libgcc/config/nds32/linux-unwind.h
blob: c8f5983c916cec4d1a86b30c0f26b738f0d4f5fc (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* DWARF2 EH unwinding support for NDS32 Linux signal frame.
   Copyright (C) 2014-2015 Free Software Foundation, Inc.
   Contributed by Andes Technology Corporation.

   This file is part of GCC.

   GCC is free software; you can redistribute it and/or modify it
   under the terms of the GNU General Public License as published
   by the Free Software Foundation; either version 3, or (at your
   option) any later version.

   GCC is distributed in the hope that it will be useful, but WITHOUT
   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
   License for more details.

   Under Section 7 of GPL version 3, you are granted additional
   permissions described in the GCC Runtime Library Exception, version
   3.1, as published by the Free Software Foundation.

   You should have received a copy of the GNU General Public License and
   a copy of the GCC Runtime Library Exception along with this program;
   see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
   <http://www.gnu.org/licenses/>.  */

#ifndef inhibit_libc

/* Do code reading to identify a signal frame, and set the frame
   state data appropriately.  See unwind-dw2.c for the structs.
   The corresponding bits in the Linux kernel are in
   arch/nds32/kernel/signal.c.  */

#include <signal.h>
#include <asm/unistd.h>
#include <sys/ucontext.h>

/* Exactly the same layout as the kernel structures, unique names.  */

/* arch/nds32/kernel/signal.c */
struct _rt_sigframe {
  siginfo_t info;
  struct ucontext_t uc;
};

#define SIGRETURN 0xeb0e0a64
#define RT_SIGRETURN 0xab150a64

#define MD_FALLBACK_FRAME_STATE_FOR nds32_fallback_frame_state

/* This function is supposed to be invoked by uw_frame_state_for()
   when there is no unwind data available.

   Generally, given the _Unwind_Context CONTEXT for a stack frame,
   we need to look up its caller and decode information into FS.
   However, if the exception handling happens within a signal handler,
   the return address of signal handler is a special module, which
   contains signal return syscall and has no FDE in the .eh_frame section.
   We need to implement MD_FALLBACK_FRAME_STATE_FOR so that we can
   unwind through signal frames.  */
static _Unwind_Reason_Code
nds32_fallback_frame_state (struct _Unwind_Context *context,
			    _Unwind_FrameState *fs)
{
  u_int32_t *pc = (u_int32_t *) context->ra;
  struct sigcontext *sc_;
  _Unwind_Ptr new_cfa;

#ifdef __NDS32_EB__
#error "Signal handler is not supported for force unwind."
#endif

  if ((_Unwind_Ptr) pc & 3)
    return _URC_END_OF_STACK;

  /* Check if we are going through a signal handler.
     See arch/nds32/kernel/signal.c implementation.
       SWI_SYS_SIGRETURN    -> (0xeb0e0a64)
       SWI_SYS_RT_SIGRETURN -> (0xab150a64)
     FIXME: Currently we only handle little endian (EL) case.  */
  if (pc[0] == SIGRETURN || pc[0] == RT_SIGRETURN)
    {
      /* Using '_sigfame' memory address to locate kernal's sigcontext.
	 The sigcontext structures in arch/nds32/include/asm/sigcontext.h.  */
      struct _rt_sigframe *rt_;
      rt_ = context->cfa;
      sc_ = &rt_->sig.uc.uc_mcontext;
    }
  else
    return _URC_END_OF_STACK;

  /* Update cfa from sigcontext.  */
  new_cfa = (_Unwind_Ptr) sc_;
  fs->regs.cfa_how = CFA_REG_OFFSET;
  fs->regs.cfa_reg = STACK_POINTER_REGNUM;
  fs->regs.cfa_offset = new_cfa - (_Unwind_Ptr) context->cfa;

#define NDS32_PUT_FS_REG(NUM, NAME) \
  (fs->regs.reg[NUM].how = REG_SAVED_OFFSET, \
   fs->regs.reg[NUM].loc.offset = (_Unwind_Ptr) &(sc_->NAME) - new_cfa)

  /* Restore all registers value.  */
  NDS32_PUT_FS_REG (0, nds32_r0);
  NDS32_PUT_FS_REG (1, nds32_r1);
  NDS32_PUT_FS_REG (2, nds32_r2);
  NDS32_PUT_FS_REG (3, nds32_r3);
  NDS32_PUT_FS_REG (4, nds32_r4);
  NDS32_PUT_FS_REG (5, nds32_r5);
  NDS32_PUT_FS_REG (6, nds32_r6);
  NDS32_PUT_FS_REG (7, nds32_r7);
  NDS32_PUT_FS_REG (8, nds32_r8);
  NDS32_PUT_FS_REG (9, nds32_r9);
  NDS32_PUT_FS_REG (10, nds32_r10);
  NDS32_PUT_FS_REG (11, nds32_r11);
  NDS32_PUT_FS_REG (12, nds32_r12);
  NDS32_PUT_FS_REG (13, nds32_r13);
  NDS32_PUT_FS_REG (14, nds32_r14);
  NDS32_PUT_FS_REG (15, nds32_r15);
  NDS32_PUT_FS_REG (16, nds32_r16);
  NDS32_PUT_FS_REG (17, nds32_r17);
  NDS32_PUT_FS_REG (18, nds32_r18);
  NDS32_PUT_FS_REG (19, nds32_r19);
  NDS32_PUT_FS_REG (20, nds32_r20);
  NDS32_PUT_FS_REG (21, nds32_r21);
  NDS32_PUT_FS_REG (22, nds32_r22);
  NDS32_PUT_FS_REG (23, nds32_r23);
  NDS32_PUT_FS_REG (24, nds32_r24);
  NDS32_PUT_FS_REG (25, nds32_r25);

  NDS32_PUT_FS_REG (28, nds32_fp);
  NDS32_PUT_FS_REG (29, nds32_gp);
  NDS32_PUT_FS_REG (30, nds32_lp);
  NDS32_PUT_FS_REG (31, nds32_sp);

  /* Restore PC, point to trigger signal instruction.  */
  NDS32_PUT_FS_REG (32, nds32_ipc);

#undef NDS32_PUT_FS_REG

  /* The retaddr is PC, use PC to find FDE.  */
  fs->retaddr_column = 32;
  fs->signal_frame = 1;

  return _URC_NO_REASON;
}

#endif