1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2003-2005 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/* Logically, we like to think of the stack as a contiguous region of
27memory.  Unfortunately, this logical view doesn't work for the
28register backing store, because the RSE is an asynchronous engine and
29because UNIX/Linux allow for stack-switching via sigaltstack(2).
30Specifically, this means that any given stacked register may or may
31not be backed up by memory in the current stack.  If not, then the
32backing memory may be found in any of the "more inner" (younger)
33stacks.  The routines in this file help manage the discontiguous
34nature of the register backing store.  The routines are completely
35independent of UNIX/Linux, but each stack frame that switches the
36backing store is expected to reserve 4 words for use by libunwind. For
37example, in the Linux sigcontext, sc_fr[0] and sc_fr[1] serve this
38purpose.  */
39
40#include "unwind_i.h"
41
42#if UNW_DEBUG
43
44HIDDEN const char *
45ia64_strloc (ia64_loc_t loc)
46{
47  static char buf[128];
48
49  if (IA64_IS_NULL_LOC (loc))
50    return "<null>";
51
52  buf[0] = '\0';
53
54  if (IA64_IS_MEMSTK_NAT (loc))
55    strcat (buf, "memstk_nat(");
56  if (IA64_IS_UC_LOC (loc))
57    strcat (buf, "uc(");
58  if (IA64_IS_FP_LOC (loc))
59    strcat (buf, "fp(");
60
61  if (IA64_IS_REG_LOC (loc))
62    sprintf (buf + strlen (buf), "%s", unw_regname (IA64_GET_REG (loc)));
63  else
64    sprintf (buf + strlen (buf), "0x%llx",
65	     (unsigned long long) IA64_GET_ADDR (loc));
66
67  if (IA64_IS_FP_LOC (loc))
68    strcat (buf, ")");
69  if (IA64_IS_UC_LOC (loc))
70    strcat (buf, ")");
71  if (IA64_IS_MEMSTK_NAT (loc))
72    strcat (buf, ")");
73
74  return buf;
75}
76
77#endif /* UNW_DEBUG */
78
79HIDDEN int
80rbs_switch (struct cursor *c,
81	    unw_word_t saved_bsp, unw_word_t saved_bspstore,
82	    ia64_loc_t saved_rnat_loc)
83{
84  struct rbs_area *rbs = &c->rbs_area[c->rbs_curr];
85  unw_word_t lo, ndirty, rbs_base;
86  int ret;
87
88  Debug (10, "(left=%u, curr=%u)\n", c->rbs_left_edge, c->rbs_curr);
89
90  /* Calculate address "lo" at which the backing store starts:  */
91  ndirty = rse_num_regs (saved_bspstore, saved_bsp);
92  lo = rse_skip_regs (c->bsp, -ndirty);
93
94  rbs->size = (rbs->end - lo);
95
96  /* If the previously-recorded rbs-area is empty we don't need to
97     track it and we can simply overwrite it... */
98  if (rbs->size)
99    {
100      Debug (10, "inner=[0x%lx-0x%lx)\n",
101	     (long) (rbs->end - rbs->size), (long) rbs->end);
102
103      c->rbs_curr = (c->rbs_curr + 1) % ARRAY_SIZE (c->rbs_area);
104      rbs = c->rbs_area + c->rbs_curr;
105
106      if (c->rbs_curr == c->rbs_left_edge)
107	c->rbs_left_edge = (c->rbs_left_edge + 1) % ARRAY_SIZE (c->rbs_area);
108    }
109
110  if ((ret = rbs_get_base (c, saved_bspstore, &rbs_base)) < 0)
111    return ret;
112
113  rbs->end = saved_bspstore;
114  rbs->size = saved_bspstore - rbs_base;
115  rbs->rnat_loc = saved_rnat_loc;
116
117  c->bsp = saved_bsp;
118
119  Debug (10, "outer=[0x%llx-0x%llx), rnat@%s\n", (long long) rbs_base,
120	 (long long) rbs->end, ia64_strloc (rbs->rnat_loc));
121  return 0;
122}
123
124HIDDEN int
125rbs_find_stacked (struct cursor *c, unw_word_t regs_to_skip,
126		  ia64_loc_t *locp, ia64_loc_t *rnat_locp)
127{
128  unw_word_t nregs, bsp = c->bsp, curr = c->rbs_curr, n;
129  unw_word_t left_edge = c->rbs_left_edge;
130#if UNW_DEBUG
131  int reg = 32 + regs_to_skip;
132#endif
133
134  while (!rbs_contains (&c->rbs_area[curr], bsp))
135    {
136      if (curr == left_edge)
137	{
138	  Debug (1, "could not find register r%d!\n", reg);
139	  return -UNW_EBADREG;
140	}
141
142      n = rse_num_regs (c->rbs_area[curr].end, bsp);
143      curr = (curr + ARRAY_SIZE (c->rbs_area) - 1) % ARRAY_SIZE (c->rbs_area);
144      bsp = rse_skip_regs (c->rbs_area[curr].end - c->rbs_area[curr].size, n);
145    }
146
147  while (1)
148    {
149      nregs = rse_num_regs (bsp, c->rbs_area[curr].end);
150
151      if (regs_to_skip < nregs)
152	{
153	  /* found it: */
154	  unw_word_t addr;
155
156	  addr = rse_skip_regs (bsp, regs_to_skip);
157	  if (locp)
158	    *locp = rbs_loc (c->rbs_area + curr, addr);
159	  if (rnat_locp)
160	    *rnat_locp = rbs_get_rnat_loc (c->rbs_area + curr, addr);
161	  return 0;
162	}
163
164      if (curr == left_edge)
165	{
166	  Debug (1, "could not find register r%d!\n", reg);
167	  return -UNW_EBADREG;
168	}
169
170      regs_to_skip -= nregs;
171
172      curr = (curr + ARRAY_SIZE (c->rbs_area) - 1) % ARRAY_SIZE (c->rbs_area);
173      bsp = c->rbs_area[curr].end - c->rbs_area[curr].size;
174    }
175}
176
177#ifdef NEED_RBS_COVER_AND_FLUSH
178
179static inline int
180get_rnat (struct cursor *c, struct rbs_area *rbs, unw_word_t bsp,
181	  unw_word_t *__restrict rnatp)
182{
183  ia64_loc_t rnat_locp = rbs_get_rnat_loc (rbs, bsp);
184
185  return ia64_get (c, rnat_locp, rnatp);
186}
187
188/* Simulate the effect of "cover" followed by a "flushrs" for the
189   target-frame.  However, since the target-frame's backing store
190   may not have space for the registers that got spilled onto other
191   rbs-areas, we save those registers to DIRTY_PARTITION where
192   we can then load them via a single "loadrs".
193
194   This function returns the size of the dirty-partition that was
195   created or a negative error-code in case of error.
196
197   Note: This does not modify the rbs_area[] structure in any way.  */
198HIDDEN int
199rbs_cover_and_flush (struct cursor *c, unw_word_t nregs,
200		     unw_word_t *dirty_partition, unw_word_t *dirty_rnat,
201		     unw_word_t *bspstore)
202{
203  unw_word_t n, src_mask, dst_mask, bsp, *dst, src_rnat, dst_rnat = 0;
204  unw_word_t curr = c->rbs_curr, left_edge = c->rbs_left_edge;
205  struct rbs_area *rbs = c->rbs_area + curr;
206  int ret;
207
208  bsp = c->bsp;
209  c->bsp = rse_skip_regs (bsp, nregs);
210
211  if (likely (rbs_contains (rbs, bsp)))
212    {
213      /* at least _some_ registers are on rbs... */
214      n = rse_num_regs (bsp, rbs->end);
215      if (likely (n >= nregs))
216	{
217	  /* common case #1: all registers are on current rbs... */
218	  /* got lucky: _all_ registers are on rbs... */
219	  ia64_loc_t rnat_loc = rbs_get_rnat_loc (rbs, c->bsp);
220
221	  *bspstore = c->bsp;
222
223	  if (IA64_IS_REG_LOC (rnat_loc))
224	    {
225	      unw_word_t rnat_addr = (unw_word_t)
226		tdep_uc_addr (c->as_arg, UNW_IA64_AR_RNAT, NULL);
227	      rnat_loc = IA64_LOC_ADDR (rnat_addr, 0);
228	    }
229	  c->loc[IA64_REG_RNAT] = rnat_loc;
230	  return 0;	/* all done */
231	}
232      nregs -= n;	/* account for registers already on the rbs */
233
234      assert (rse_skip_regs (c->bsp, -nregs) == rse_skip_regs (rbs->end, 0));
235    }
236  else
237    /* Earlier frames also didn't get spilled; need to "loadrs" those,
238       too... */
239    nregs += rse_num_regs (rbs->end, bsp);
240
241  /* OK, we need to copy NREGS registers to the dirty partition.  */
242
243  *bspstore = bsp = rbs->end;
244  c->loc[IA64_REG_RNAT] = rbs->rnat_loc;
245  assert (!IA64_IS_REG_LOC (rbs->rnat_loc));
246
247  dst = dirty_partition;
248
249  while (nregs > 0)
250    {
251      if (unlikely (!rbs_contains (rbs, bsp)))
252	{
253	  /* switch to next non-empty rbs-area: */
254	  do
255	    {
256	      if (curr == left_edge)
257		{
258		  Debug (0, "rbs-underflow while flushing %lu regs, "
259			 "bsp=0x%lx, dst=0x%p\n", (unsigned long) nregs,
260			 (unsigned long) bsp, dst);
261		  return -UNW_EBADREG;
262		}
263
264	      assert (rse_num_regs (rbs->end, bsp) == 0);
265
266	      curr = (curr + ARRAY_SIZE (c->rbs_area) - 1)
267		      % ARRAY_SIZE (c->rbs_area);
268	      rbs = c->rbs_area + curr;
269	      bsp = rbs->end - rbs->size;
270	    }
271	  while (rbs->size == 0);
272
273	  if ((ret = get_rnat (c, rbs, bsp, &src_rnat)) < 0)
274	    return ret;
275	}
276
277      if (unlikely (rse_is_rnat_slot (bsp)))
278	{
279	  bsp += 8;
280	  if ((ret = get_rnat (c, rbs, bsp, &src_rnat)) < 0)
281	    return ret;
282	}
283      if (unlikely (rse_is_rnat_slot ((unw_word_t) dst)))
284	{
285	  *dst++ = dst_rnat;
286	  dst_rnat = 0;
287	}
288
289      src_mask = ((unw_word_t) 1) << rse_slot_num (bsp);
290      dst_mask = ((unw_word_t) 1) << rse_slot_num ((unw_word_t) dst);
291
292      if (src_rnat & src_mask)
293	dst_rnat |= dst_mask;
294      else
295	dst_rnat &= ~dst_mask;
296
297      /* copy one slot: */
298      if ((ret = ia64_get (c, rbs_loc (rbs, bsp), dst)) < 0)
299	return ret;
300
301      /* advance to next slot: */
302      --nregs;
303      bsp += 8;
304      ++dst;
305    }
306  if (unlikely (rse_is_rnat_slot ((unw_word_t) dst)))
307    {
308      /* The LOADRS instruction loads "the N bytes below the current
309	 BSP" but BSP can never point to an RNaT slot so if the last
310	 destination word happens to be an RNaT slot, we need to write
311	 that slot now. */
312      *dst++ = dst_rnat;
313      dst_rnat = 0;
314    }
315  *dirty_rnat = dst_rnat;
316  return (char *) dst - (char *) dirty_partition;
317}
318
319#endif /* !UNW_REMOTE_ONLY */
320