1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2001-2004 Hewlett-Packard Co
3	Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4
5This file is part of libunwind.
6
7Permission is hereby granted, free of charge, to any person obtaining
8a copy of this software and associated documentation files (the
9"Software"), to deal in the Software without restriction, including
10without limitation the rights to use, copy, modify, merge, publish,
11distribute, sublicense, and/or sell copies of the Software, and to
12permit persons to whom the Software is furnished to do so, subject to
13the following conditions:
14
15The above copyright notice and this permission notice shall be
16included in all copies or substantial portions of the Software.
17
18THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
25
26#include <stdlib.h>
27
28#include "unwind_i.h"
29#include "offsets.h"
30
31#ifndef UNW_REMOTE_ONLY
32
33static inline int
34local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg)
35{
36#if defined(__linux)
37  unw_word_t dirty_partition[2048]; /* AR.RSC.LOADRS is a 14-bit field */
38  unw_word_t val, sol, sof, pri_unat, n, pfs, bspstore, dirty_rnat;
39  struct cursor *c = (struct cursor *) cursor;
40  struct
41    {
42      unw_word_t r1;
43      unw_word_t r4;
44      unw_word_t r5;
45      unw_word_t r6;
46      unw_word_t r7;
47      unw_word_t r15;
48      unw_word_t r16;
49      unw_word_t r17;
50      unw_word_t r18;
51    }
52  extra;
53  int ret, dirty_size;
54# define GET_NAT(n)						\
55  do								\
56    {								\
57      ret = tdep_access_reg (c, UNW_IA64_NAT + (n), &val, 0);	\
58      if (ret < 0)						\
59	return ret;						\
60      if (val)							\
61	pri_unat |= (unw_word_t) 1 << n;			\
62    }								\
63  while (0)
64
65  /* ensure c->pi is up-to-date: */
66  if ((ret = ia64_make_proc_info (c)) < 0)
67    return ret;
68
69  /* Copy contents of r4-r7 into "extra", so that their values end up
70     contiguous, so we can use a single (primary-) UNaT value.  */
71  if ((ret = ia64_get (c, c->loc[IA64_REG_R4], &extra.r4)) < 0
72      || (ret = ia64_get (c, c->loc[IA64_REG_R5], &extra.r5)) < 0
73      || (ret = ia64_get (c, c->loc[IA64_REG_R6], &extra.r6)) < 0
74      || (ret = ia64_get (c, c->loc[IA64_REG_R7], &extra.r7)) < 0)
75    return ret;
76
77  /* Form the primary UNaT value: */
78  pri_unat = 0;
79  GET_NAT (4); GET_NAT(5);
80  GET_NAT (6); GET_NAT(7);
81  n = (((uintptr_t) &extra.r4) / 8 - 4) % 64;
82  pri_unat = (pri_unat << n) | (pri_unat >> (64 - n));
83
84  if (unlikely (c->sigcontext_addr))
85    {
86      struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr;
87#     define PR_SCRATCH		0xffc0	/* p6-p15 are scratch */
88#     define PR_PRESERVED	(~(PR_SCRATCH | 1))
89
90      /* We're returning to a frame that was (either directly or
91	 indirectly) interrupted by a signal.  We have to restore
92	 _both_ "preserved" and "scratch" registers.  That doesn't
93	 leave us any registers to work with, and the only way we can
94	 achieve this is by doing a sigreturn().
95
96	 Note: it might be tempting to think that we don't have to
97	 restore the scratch registers when returning to a frame that
98	 was indirectly interrupted by a signal.  However, that is not
99	 safe because that frame and its descendants could have been
100	 using a special convention that stores "preserved" state in
101	 scratch registers.  For example, the Linux fsyscall
102	 convention does this with r11 (to save ar.pfs) and b6 (to
103	 save "rp"). */
104
105      sc->sc_gr[12] = c->psp;
106      c->psp = c->sigcontext_addr - c->sigcontext_off;
107
108      sof = (c->cfm & 0x7f);
109      if ((dirty_size = rbs_cover_and_flush (c, sof, dirty_partition,
110					     &dirty_rnat, &bspstore)) < 0)
111	return dirty_size;
112
113      /* Clear the "in-syscall" flag, because in general we won't be
114	 returning to the interruption-point and we need all registers
115	 restored.  */
116      sc->sc_flags &= ~IA64_SC_FLAG_IN_SYSCALL;
117      sc->sc_ip = c->ip;
118      sc->sc_cfm = c->cfm & (((unw_word_t) 1 << 38) - 1);
119      sc->sc_pr = (c->pr & ~PR_SCRATCH) | (sc->sc_pr & ~PR_PRESERVED);
120      if ((ret = ia64_get (c, c->loc[IA64_REG_PFS], &sc->sc_ar_pfs)) < 0
121	  || (ret = ia64_get (c, c->loc[IA64_REG_FPSR], &sc->sc_ar_fpsr)) < 0
122	  || (ret = ia64_get (c, c->loc[IA64_REG_UNAT], &sc->sc_ar_unat)) < 0)
123	return ret;
124
125      sc->sc_gr[1] = c->pi.gp;
126      if (c->eh_valid_mask & 0x1) sc->sc_gr[15] = c->eh_args[0];
127      if (c->eh_valid_mask & 0x2) sc->sc_gr[16] = c->eh_args[1];
128      if (c->eh_valid_mask & 0x4) sc->sc_gr[17] = c->eh_args[2];
129      if (c->eh_valid_mask & 0x8) sc->sc_gr[18] = c->eh_args[3];
130      Debug (9, "sc: r15=%lx,r16=%lx,r17=%lx,r18=%lx\n",
131	     (long) sc->sc_gr[15], (long) sc->sc_gr[16],
132	     (long) sc->sc_gr[17], (long) sc->sc_gr[18]);
133    }
134  else
135    {
136      /* Account for the fact that _Uia64_install_context() will
137	 return via br.ret, which will decrement bsp by size-of-locals.  */
138      if ((ret = ia64_get (c, c->loc[IA64_REG_PFS], &pfs)) < 0)
139	return ret;
140      sol = (pfs >> 7) & 0x7f;
141      if ((dirty_size = rbs_cover_and_flush (c, sol, dirty_partition,
142					     &dirty_rnat, &bspstore)) < 0)
143	return dirty_size;
144
145      extra.r1 = c->pi.gp;
146      extra.r15 = c->eh_args[0];
147      extra.r16 = c->eh_args[1];
148      extra.r17 = c->eh_args[2];
149      extra.r18 = c->eh_args[3];
150      Debug (9, "extra: r15=%lx,r16=%lx,r17=%lx,r18=%lx\n",
151	     (long) extra.r15, (long) extra.r16,
152	     (long) extra.r17, (long) extra.r18);
153    }
154  Debug (8, "resuming at ip=%lx\n", (long) c->ip);
155  ia64_install_cursor (c, pri_unat, (unw_word_t *) &extra,
156		       bspstore, dirty_size, dirty_partition + dirty_size/8,
157		       dirty_rnat);
158#elif defined(__hpux)
159  struct cursor *c = (struct cursor *) cursor;
160
161  setcontext (c->as_arg);	/* should not return */
162#endif
163  return -UNW_EINVAL;
164}
165
166HIDDEN int
167ia64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg)
168{
169  return local_resume (as, cursor, arg);
170}
171
172#endif /* !UNW_REMOTE_ONLY */
173
174#ifndef UNW_LOCAL_ONLY
175
176static inline int
177remote_install_cursor (struct cursor *c)
178{
179  int (*access_reg) (unw_addr_space_t, unw_regnum_t, unw_word_t *,
180		     int write, void *);
181  int (*access_fpreg) (unw_addr_space_t, unw_regnum_t, unw_fpreg_t *,
182		       int write, void *);
183  unw_fpreg_t fpval;
184  unw_word_t val;
185  int reg;
186
187#if defined(__linux) && !defined(UNW_REMOTE_ONLY)
188  if (c->as == unw_local_addr_space)
189    {
190      /* Take a short-cut: we directly resume out of the cursor and
191	 all we need to do is make sure that all locations point to
192	 memory, not registers.  Furthermore, R4-R7 and NAT4-NAT7 are
193	 taken care of by ia64_local_resume() so they don't need to be
194	 handled here.  */
195#     define MEMIFY(preg, reg)						 \
196      do {								 \
197	if (IA64_IS_REG_LOC (c->loc[(preg)]))				 \
198	  c->loc[(preg)] = IA64_LOC_ADDR ((unw_word_t)			 \
199					  tdep_uc_addr(c->as_arg, (reg), \
200						       NULL), 0);	 \
201      } while (0)
202      MEMIFY (IA64_REG_PR,	UNW_IA64_PR);
203      MEMIFY (IA64_REG_PFS,	UNW_IA64_AR_PFS);
204      MEMIFY (IA64_REG_RNAT,	UNW_IA64_AR_RNAT);
205      MEMIFY (IA64_REG_UNAT,	UNW_IA64_AR_UNAT);
206      MEMIFY (IA64_REG_LC,	UNW_IA64_AR_LC);
207      MEMIFY (IA64_REG_FPSR,	UNW_IA64_AR_FPSR);
208      MEMIFY (IA64_REG_IP,	UNW_IA64_BR + 0);
209      MEMIFY (IA64_REG_B1,	UNW_IA64_BR + 1);
210      MEMIFY (IA64_REG_B2,	UNW_IA64_BR + 2);
211      MEMIFY (IA64_REG_B3,	UNW_IA64_BR + 3);
212      MEMIFY (IA64_REG_B4,	UNW_IA64_BR + 4);
213      MEMIFY (IA64_REG_B5,	UNW_IA64_BR + 5);
214      MEMIFY (IA64_REG_F2,	UNW_IA64_FR + 2);
215      MEMIFY (IA64_REG_F3,	UNW_IA64_FR + 3);
216      MEMIFY (IA64_REG_F4,	UNW_IA64_FR + 4);
217      MEMIFY (IA64_REG_F5,	UNW_IA64_FR + 5);
218      MEMIFY (IA64_REG_F16,	UNW_IA64_FR + 16);
219      MEMIFY (IA64_REG_F17,	UNW_IA64_FR + 17);
220      MEMIFY (IA64_REG_F18,	UNW_IA64_FR + 18);
221      MEMIFY (IA64_REG_F19,	UNW_IA64_FR + 19);
222      MEMIFY (IA64_REG_F20,	UNW_IA64_FR + 20);
223      MEMIFY (IA64_REG_F21,	UNW_IA64_FR + 21);
224      MEMIFY (IA64_REG_F22,	UNW_IA64_FR + 22);
225      MEMIFY (IA64_REG_F23,	UNW_IA64_FR + 23);
226      MEMIFY (IA64_REG_F24,	UNW_IA64_FR + 24);
227      MEMIFY (IA64_REG_F25,	UNW_IA64_FR + 25);
228      MEMIFY (IA64_REG_F26,	UNW_IA64_FR + 26);
229      MEMIFY (IA64_REG_F27,	UNW_IA64_FR + 27);
230      MEMIFY (IA64_REG_F28,	UNW_IA64_FR + 28);
231      MEMIFY (IA64_REG_F29,	UNW_IA64_FR + 29);
232      MEMIFY (IA64_REG_F30,	UNW_IA64_FR + 30);
233      MEMIFY (IA64_REG_F31,	UNW_IA64_FR + 31);
234    }
235  else
236#endif /* __linux && !UNW_REMOTE_ONLY */
237    {
238      access_reg = c->as->acc.access_reg;
239      access_fpreg = c->as->acc.access_fpreg;
240
241      Debug (8, "copying out cursor state\n");
242
243      for (reg = 0; reg <= UNW_REG_LAST; ++reg)
244	{
245	  if (unw_is_fpreg (reg))
246	    {
247	      if (tdep_access_fpreg (c, reg, &fpval, 0) >= 0)
248		(*access_fpreg) (c->as, reg, &fpval, 1, c->as_arg);
249	    }
250	  else
251	    {
252	      if (tdep_access_reg (c, reg, &val, 0) >= 0)
253		(*access_reg) (c->as, reg, &val, 1, c->as_arg);
254	    }
255	}
256    }
257  return (*c->as->acc.resume) (c->as, (unw_cursor_t *) c, c->as_arg);
258}
259
260#endif /* !UNW_LOCAL_ONLY */
261
262PROTECTED int
263unw_resume (unw_cursor_t *cursor)
264{
265  struct cursor *c = (struct cursor *) cursor;
266
267  Debug (1, "(cursor=%p, ip=0x%016lx)\n", c, (unsigned long) c->ip);
268
269#ifdef UNW_LOCAL_ONLY
270  return local_resume (c->as, cursor, c->as_arg);
271#else
272  return remote_install_cursor (c);
273#endif
274}
275