blob: 47825659dc4a62fddb69ac41416cf4b64fb46d80 [file] [log] [blame]
mostang.com!davidm997f0252002-12-19 07:16:50 +00001/* libunwind - a platform-independent unwind library
hp.com!davidmc67d3452004-01-30 00:01:24 +00002 Copyright (C) 2001-2004 Hewlett-Packard Co
mostang.com!davidm997f0252002-12-19 07:16:50 +00003 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 "rse.h"
29#include "unwind_i.h"
mostang.com!davidmea0a71a2003-01-21 17:41:20 +000030#include "offsets.h"
mostang.com!davidm997f0252002-12-19 07:16:50 +000031
32#ifndef UNW_REMOTE_ONLY
33
mostang.com!davidme4f251b2003-03-28 07:43:22 +000034static inline int
35local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg)
mostang.com!davidm997f0252002-12-19 07:16:50 +000036{
mostang.com!davidm3d3510c2003-04-24 20:45:07 +000037#if defined(__linux)
mostang.com!davidmb8c17812003-02-15 08:49:55 +000038 unw_word_t val, sol, sof, pri_unat, n, pfs;
mostang.com!davidm997f0252002-12-19 07:16:50 +000039 struct cursor *c = (struct cursor *) cursor;
mostang.com!davidm678d3202003-02-08 10:10:59 +000040 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;
54# define GET_NAT(n) \
mostang.com!davidm997f0252002-12-19 07:16:50 +000055 do \
56 { \
hp.com!davidmc67d3452004-01-30 00:01:24 +000057 ret = tdep_access_reg (c, UNW_IA64_NAT + (n), &val, 0); \
mostang.com!davidm997f0252002-12-19 07:16:50 +000058 if (ret < 0) \
59 return ret; \
60 if (val) \
mostang.com!davidm678d3202003-02-08 10:10:59 +000061 pri_unat |= (unw_word_t) 1 << n; \
mostang.com!davidm997f0252002-12-19 07:16:50 +000062 } \
63 while (0)
mostang.com!davidm997f0252002-12-19 07:16:50 +000064
65 /* ensure c->pi is up-to-date: */
66 if ((ret = ia64_make_proc_info (c)) < 0)
67 return ret;
68
mostang.com!davidm678d3202003-02-08 10:10:59 +000069 /* Copy contents of r4-r7 into "extra", so that their values end up
70 contiguous, so we can use a single (primary-) UNaT value. */
mostang.com!davidmc1d25572003-04-23 05:56:59 +000071 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)
mostang.com!davidm678d3202003-02-08 10:10:59 +000075 return ret;
mostang.com!davidm997f0252002-12-19 07:16:50 +000076
mostang.com!davidm678d3202003-02-08 10:10:59 +000077 /* 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));
mostang.com!davidm997f0252002-12-19 07:16:50 +000083
mostang.com!davidmc1d25572003-04-23 05:56:59 +000084 if (unlikely (c->sigcontext_addr))
mostang.com!davidmea0a71a2003-01-21 17:41:20 +000085 {
mostang.com!davidmc1d25572003-04-23 05:56:59 +000086 struct sigcontext *sc = (struct sigcontext *) c->sigcontext_addr;
mostang.com!davidm678d3202003-02-08 10:10:59 +000087# define PR_SCRATCH 0xffc0 /* p6-p15 are scratch */
88# define PR_PRESERVED (~(PR_SCRATCH | 1))
mostang.com!davidmea0a71a2003-01-21 17:41:20 +000089
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
mostang.com!davidm678d3202003-02-08 10:10:59 +0000101 scratch registers. For example, the Linux fsyscall
102 convention does this with r11 (to save ar.pfs) and b6 (to
103 save "rp"). */
mostang.com!davidmea0a71a2003-01-21 17:41:20 +0000104
mostang.com!davidm678d3202003-02-08 10:10:59 +0000105 sc->sc_gr[12] = c->psp;
mostang.com!davidmc1d25572003-04-23 05:56:59 +0000106 c->psp = c->sigcontext_addr - c->sigcontext_off;
mostang.com!davidm678d3202003-02-08 10:10:59 +0000107
108 sof = (c->cfm & 0x7f);
mostang.com!davidm02c8c0c2003-02-14 06:25:36 +0000109 rbs_cover_and_flush (c, sof);
mostang.com!davidm678d3202003-02-08 10:10:59 +0000110
hp.com!davidm350ae982003-09-25 05:08:18 +0000111 /* Clear the "in-syscall" flag, because in general we won't be
112 returning to the interruption-point and we need all registers
113 restored. */
114 sc->sc_flags &= ~IA64_SC_FLAG_IN_SYSCALL;
mostang.com!davidm678d3202003-02-08 10:10:59 +0000115 sc->sc_ip = c->ip;
116 sc->sc_cfm = c->cfm & (((unw_word_t) 1 << 38) - 1);
117 sc->sc_pr = (c->pr & ~PR_SCRATCH) | (sc->sc_pr & ~PR_PRESERVED);
mostang.com!davidmc1d25572003-04-23 05:56:59 +0000118 if ((ret = ia64_get (c, c->loc[IA64_REG_PFS], &sc->sc_ar_pfs)) < 0
119 || (ret = ia64_get (c, c->loc[IA64_REG_FPSR], &sc->sc_ar_fpsr)) < 0
120 || (ret = ia64_get (c, c->loc[IA64_REG_UNAT], &sc->sc_ar_unat)) < 0)
mostang.com!davidm678d3202003-02-08 10:10:59 +0000121 return ret;
122
123 sc->sc_gr[1] = c->pi.gp;
124 if (c->eh_valid_mask & 0x1) sc->sc_gr[15] = c->eh_args[0];
125 if (c->eh_valid_mask & 0x2) sc->sc_gr[16] = c->eh_args[1];
126 if (c->eh_valid_mask & 0x4) sc->sc_gr[17] = c->eh_args[2];
127 if (c->eh_valid_mask & 0x8) sc->sc_gr[18] = c->eh_args[3];
hp.com!davidmc67d3452004-01-30 00:01:24 +0000128 Debug (9, "sc: r15=%lx,r16=%lx,r17=%lx,r18=%lx\n",
129 (long) sc->sc_gr[15], (long) sc->sc_gr[16],
hp.com!davidm350ae982003-09-25 05:08:18 +0000130 (long) sc->sc_gr[17], (long) sc->sc_gr[18]);
mostang.com!davidmea0a71a2003-01-21 17:41:20 +0000131 }
132 else
133 {
mostang.com!davidm678d3202003-02-08 10:10:59 +0000134 /* Account for the fact that _Uia64_install_context() will
135 return via br.ret, which will decrement bsp by size-of-locals. */
mostang.com!davidmc1d25572003-04-23 05:56:59 +0000136 if ((ret = ia64_get (c, c->loc[IA64_REG_PFS], &pfs)) < 0)
mostang.com!davidm678d3202003-02-08 10:10:59 +0000137 return ret;
138 sol = (pfs >> 7) & 0x7f;
mostang.com!davidm02c8c0c2003-02-14 06:25:36 +0000139 rbs_cover_and_flush (c, sol);
mostang.com!davidm678d3202003-02-08 10:10:59 +0000140
141 extra.r1 = c->pi.gp;
142 extra.r15 = c->eh_args[0];
143 extra.r16 = c->eh_args[1];
144 extra.r17 = c->eh_args[2];
145 extra.r18 = c->eh_args[3];
hp.com!davidmc67d3452004-01-30 00:01:24 +0000146 Debug (9, "extra: r15=%lx,r16=%lx,r17=%lx,r18=%lx\n",
147 (long) extra.r15, (long) extra.r16,
hp.com!davidm350ae982003-09-25 05:08:18 +0000148 (long) extra.r17, (long) extra.r18);
mostang.com!davidmea0a71a2003-01-21 17:41:20 +0000149 }
hp.com!davidmc67d3452004-01-30 00:01:24 +0000150 Debug (8, "resuming at ip=%lx\n", (long) c->ip);
mostang.com!davidm6c584b72003-04-23 19:22:42 +0000151 ia64_install_cursor (c, pri_unat, (unw_word_t *) &extra);
mostang.com!davidm3d3510c2003-04-24 20:45:07 +0000152#elif defined(__hpux)
153 struct cursor *c = (struct cursor *) cursor;
154
155 setcontext (c->as_arg); /* should not return */
156#endif
157 return -UNW_EINVAL;
mostang.com!davidm997f0252002-12-19 07:16:50 +0000158}
159
mostang.com!davidme4f251b2003-03-28 07:43:22 +0000160HIDDEN int
161ia64_local_resume (unw_addr_space_t as, unw_cursor_t *cursor, void *arg)
162{
mostang.com!davidm6c584b72003-04-23 19:22:42 +0000163 return local_resume (as, cursor, arg);
mostang.com!davidme4f251b2003-03-28 07:43:22 +0000164}
165
mostang.com!davidm997f0252002-12-19 07:16:50 +0000166#endif /* !UNW_REMOTE_ONLY */
167
mostang.com!davidm678d3202003-02-08 10:10:59 +0000168#ifndef UNW_LOCAL_ONLY
169
170static inline int
171remote_install_cursor (struct cursor *c)
172{
hp.com!davidm2b0b48f2003-02-22 03:08:22 +0000173 int (*access_reg) (unw_addr_space_t, unw_regnum_t, unw_word_t *,
174 int write, void *);
175 int (*access_fpreg) (unw_addr_space_t, unw_regnum_t, unw_fpreg_t *,
176 int write, void *);
177 unw_fpreg_t fpval;
178 unw_word_t val;
179 int reg;
180
hp.com!davidmdc34eda2003-04-25 07:44:23 +0000181#if defined(__linux) && !defined(UNW_REMOTE_ONLY)
hp.com!davidm2b0b48f2003-02-22 03:08:22 +0000182 if (c->as == unw_local_addr_space)
183 {
184 /* Take a short-cut: we directly resume out of the cursor and
185 all we need to do is make sure that all locations point to
186 memory, not registers. Furthermore, R4-R7 and NAT4-NAT7 are
187 taken care of by ia64_local_resume() so they don't need to be
188 handled here. */
mostang.com!davidm5de46072004-02-27 08:54:25 +0000189# define MEMIFY(preg, reg) \
190 do { \
191 if (IA64_IS_REG_LOC (c->loc[(preg)])) \
192 c->loc[(preg)] = IA64_LOC_ADDR ((unw_word_t) \
193 tdep_uc_addr(c->as_arg, (reg), \
194 NULL), 0); \
hp.com!davidm2b0b48f2003-02-22 03:08:22 +0000195 } while (0)
196 MEMIFY (IA64_REG_PR, UNW_IA64_PR);
197 MEMIFY (IA64_REG_PFS, UNW_IA64_AR_PFS);
198 MEMIFY (IA64_REG_RNAT, UNW_IA64_AR_RNAT);
199 MEMIFY (IA64_REG_UNAT, UNW_IA64_AR_UNAT);
200 MEMIFY (IA64_REG_LC, UNW_IA64_AR_LC);
201 MEMIFY (IA64_REG_FPSR, UNW_IA64_AR_FPSR);
mostang.com!davidmc1d25572003-04-23 05:56:59 +0000202 MEMIFY (IA64_REG_IP, UNW_IA64_BR + 0);
hp.com!davidm2b0b48f2003-02-22 03:08:22 +0000203 MEMIFY (IA64_REG_B1, UNW_IA64_BR + 1);
204 MEMIFY (IA64_REG_B2, UNW_IA64_BR + 2);
205 MEMIFY (IA64_REG_B3, UNW_IA64_BR + 3);
206 MEMIFY (IA64_REG_B4, UNW_IA64_BR + 4);
207 MEMIFY (IA64_REG_B5, UNW_IA64_BR + 5);
208 MEMIFY (IA64_REG_F2, UNW_IA64_FR + 2);
209 MEMIFY (IA64_REG_F3, UNW_IA64_FR + 3);
210 MEMIFY (IA64_REG_F4, UNW_IA64_FR + 4);
211 MEMIFY (IA64_REG_F5, UNW_IA64_FR + 5);
212 MEMIFY (IA64_REG_F16, UNW_IA64_FR + 16);
213 MEMIFY (IA64_REG_F17, UNW_IA64_FR + 17);
214 MEMIFY (IA64_REG_F18, UNW_IA64_FR + 18);
215 MEMIFY (IA64_REG_F19, UNW_IA64_FR + 19);
216 MEMIFY (IA64_REG_F20, UNW_IA64_FR + 20);
217 MEMIFY (IA64_REG_F21, UNW_IA64_FR + 21);
218 MEMIFY (IA64_REG_F22, UNW_IA64_FR + 22);
219 MEMIFY (IA64_REG_F23, UNW_IA64_FR + 23);
220 MEMIFY (IA64_REG_F24, UNW_IA64_FR + 24);
221 MEMIFY (IA64_REG_F25, UNW_IA64_FR + 25);
222 MEMIFY (IA64_REG_F26, UNW_IA64_FR + 26);
223 MEMIFY (IA64_REG_F27, UNW_IA64_FR + 27);
224 MEMIFY (IA64_REG_F28, UNW_IA64_FR + 28);
225 MEMIFY (IA64_REG_F29, UNW_IA64_FR + 29);
226 MEMIFY (IA64_REG_F30, UNW_IA64_FR + 30);
227 MEMIFY (IA64_REG_F31, UNW_IA64_FR + 31);
228 }
229 else
mostang.com!davidm3d3510c2003-04-24 20:45:07 +0000230#endif /* __linux */
hp.com!davidm2b0b48f2003-02-22 03:08:22 +0000231 {
232 access_reg = c->as->acc.access_reg;
233 access_fpreg = c->as->acc.access_fpreg;
234
hp.com!davidmc67d3452004-01-30 00:01:24 +0000235 Debug (8, "copying out cursor state\n");
mostang.com!davidm3d3510c2003-04-24 20:45:07 +0000236
hp.com!davidm2b0b48f2003-02-22 03:08:22 +0000237 for (reg = 0; reg < UNW_REG_LAST; ++reg)
238 {
239 if (unw_is_fpreg (reg))
240 {
hp.com!davidmc67d3452004-01-30 00:01:24 +0000241 if (tdep_access_fpreg (c, reg, &fpval, 0) >= 0)
hp.com!davidm2b0b48f2003-02-22 03:08:22 +0000242 (*access_fpreg) (c->as, reg, &fpval, 1, c->as_arg);
243 }
244 else
245 {
hp.com!davidmc67d3452004-01-30 00:01:24 +0000246 if (tdep_access_reg (c, reg, &val, 0) >= 0)
hp.com!davidm2b0b48f2003-02-22 03:08:22 +0000247 (*access_reg) (c->as, reg, &val, 1, c->as_arg);
248 }
249 }
250 }
251 return (*c->as->acc.resume) (c->as, (unw_cursor_t *) c, c->as_arg);
mostang.com!davidm678d3202003-02-08 10:10:59 +0000252}
253
mostang.com!davidm6c584b72003-04-23 19:22:42 +0000254#endif /* !UNW_LOCAL_ONLY */
mostang.com!davidm678d3202003-02-08 10:10:59 +0000255
hp.com!davidmc4f59742004-01-03 10:50:24 +0000256PROTECTED int
mostang.com!davidm997f0252002-12-19 07:16:50 +0000257unw_resume (unw_cursor_t *cursor)
258{
259 struct cursor *c = (struct cursor *) cursor;
260
hp.com!davidmc67d3452004-01-30 00:01:24 +0000261 Debug (2, "(cursor=%p)\n", c);
262
mostang.com!davidm997f0252002-12-19 07:16:50 +0000263#ifdef UNW_LOCAL_ONLY
mostang.com!davidme4f251b2003-03-28 07:43:22 +0000264 return local_resume (c->as, cursor, c->as_arg);
mostang.com!davidm997f0252002-12-19 07:16:50 +0000265#else
mostang.com!davidm3d3510c2003-04-24 20:45:07 +0000266 return remote_install_cursor (c);
mostang.com!davidm997f0252002-12-19 07:16:50 +0000267#endif
268}