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
5 This file is part of libunwind.
6
7 Permission is hereby granted, free of charge, to any person obtaining
8 a copy of this software and associated documentation files (the
9 "Software"), to deal in the Software without restriction, including
10 without limitation the rights to use, copy, modify, merge, publish,
11 distribute, sublicense, and/or sell copies of the Software, and to
12 permit persons to whom the Software is furnished to do so, subject to
13 the following conditions:
14
15 The above copyright notice and this permission notice shall be
16 included in all copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 WITH 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
33 static inline int
local_resume(unw_addr_space_t as,unw_cursor_t * cursor,void * arg)34 local_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
166 HIDDEN int
ia64_local_resume(unw_addr_space_t as,unw_cursor_t * cursor,void * arg)167 ia64_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
176 static inline int
remote_install_cursor(struct cursor * c)177 remote_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
262 int
unw_resume(unw_cursor_t * cursor)263 unw_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