2004-08-17 17:34:28 +02:00
|
|
|
/* libunwind - a platform-independent unwind library
|
|
|
|
Copyright (C) 2003-2004 Hewlett-Packard Co
|
|
|
|
Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
|
|
|
|
|
|
|
|
This file is part of libunwind.
|
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
a copy of this software and associated documentation files (the
|
|
|
|
"Software"), to deal in the Software without restriction, including
|
|
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
the following conditions:
|
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be
|
|
|
|
included in all copies or substantial portions of the Software.
|
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
|
|
|
|
|
|
|
/* Logically, we like to think of the stack as a contiguous region of
|
|
|
|
memory. Unfortunately, this logical view doesn't work for the
|
|
|
|
register backing store, because the RSE is an asynchronous engine and
|
|
|
|
because UNIX/Linux allow for stack-switching via sigaltstack(2).
|
|
|
|
Specifically, this means that any given stacked register may or may
|
|
|
|
not be backed up by memory in the current stack. If not, then the
|
|
|
|
backing memory may be found in any of the "more inner" (younger)
|
|
|
|
stacks. The routines in this file help manage the discontiguous
|
|
|
|
nature of the register backing store. The routines are completely
|
|
|
|
independent of UNIX/Linux, but each stack frame that switches the
|
|
|
|
backing store is expected to reserve 4 words for use by libunwind. For
|
|
|
|
example, in the Linux sigcontext, sc_fr[0] and sc_fr[1] serve this
|
|
|
|
purpose. */
|
|
|
|
|
|
|
|
#include "unwind_i.h"
|
|
|
|
|
|
|
|
HIDDEN int
|
|
|
|
rbs_switch (struct cursor *c,
|
|
|
|
unw_word_t saved_bsp, unw_word_t saved_bspstore,
|
|
|
|
ia64_loc_t saved_rnat_loc)
|
|
|
|
{
|
|
|
|
struct rbs_area *rbs = &c->rbs_area[c->rbs_curr];
|
|
|
|
unw_word_t lo, ndirty;
|
|
|
|
|
2004-08-26 12:02:46 +02:00
|
|
|
Debug (10, "(left=%u, curr=%u)\n", c->rbs_left_edge, c->rbs_curr);
|
2004-08-17 17:34:28 +02:00
|
|
|
|
|
|
|
/* Calculate address "lo" at which the backing store starts: */
|
|
|
|
ndirty = ia64_rse_num_regs (saved_bspstore, saved_bsp);
|
|
|
|
lo = ia64_rse_skip_regs (c->bsp, -ndirty);
|
|
|
|
|
|
|
|
rbs->size = (rbs->end - lo);
|
|
|
|
|
|
|
|
/* If the previously-recorded rbs-area is empty we don't need to
|
|
|
|
track it and we can simply overwrite it... */
|
|
|
|
if (rbs->size)
|
|
|
|
{
|
2004-08-26 12:02:46 +02:00
|
|
|
Debug (10, "inner=[0x%lx-0x%lx)\n",
|
2004-08-17 17:34:28 +02:00
|
|
|
(long) (rbs->end - rbs->size), (long) rbs->end);
|
|
|
|
|
|
|
|
c->rbs_curr = (c->rbs_curr + 1) % NELEMS (c->rbs_area);
|
|
|
|
rbs = c->rbs_area + c->rbs_curr;
|
|
|
|
|
|
|
|
if (c->rbs_curr == c->rbs_left_edge)
|
|
|
|
c->rbs_left_edge = (c->rbs_left_edge + 1) % NELEMS (c->rbs_area);
|
|
|
|
}
|
|
|
|
rbs->end = saved_bspstore;
|
|
|
|
rbs->size = ((unw_word_t) 1) << 63; /* initial guess... */
|
|
|
|
rbs->rnat_loc = saved_rnat_loc;
|
|
|
|
|
|
|
|
c->bsp = saved_bsp;
|
|
|
|
|
|
|
|
Debug (10, "outer=[?????????????????\?-0x%llx), rnat@%s\n",
|
|
|
|
(long long) rbs->end, ia64_strloc (rbs->rnat_loc));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
HIDDEN int
|
|
|
|
rbs_find_stacked (struct cursor *c, unw_word_t regs_to_skip,
|
|
|
|
ia64_loc_t *locp, ia64_loc_t *rnat_locp)
|
|
|
|
{
|
|
|
|
unw_word_t nregs, bsp = c->bsp, curr = c->rbs_curr, n;
|
|
|
|
unw_word_t left_edge = c->rbs_left_edge;
|
|
|
|
#if UNW_DEBUG
|
|
|
|
int reg = 32 + regs_to_skip;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
while (!rbs_contains (&c->rbs_area[curr], bsp))
|
|
|
|
{
|
|
|
|
if (curr == left_edge)
|
|
|
|
{
|
|
|
|
Debug (1, "could not find register r%d!\n", reg);
|
|
|
|
return -UNW_EBADREG;
|
|
|
|
}
|
|
|
|
|
|
|
|
n = ia64_rse_num_regs (c->rbs_area[curr].end, bsp);
|
|
|
|
curr = (curr + NELEMS (c->rbs_area) - 1) % NELEMS (c->rbs_area);
|
|
|
|
bsp = ia64_rse_skip_regs (c->rbs_area[curr].end - c->rbs_area[curr].size,
|
|
|
|
n);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
nregs = ia64_rse_num_regs (bsp, c->rbs_area[curr].end);
|
|
|
|
|
|
|
|
if (regs_to_skip < nregs)
|
|
|
|
{
|
|
|
|
/* found it: */
|
|
|
|
unw_word_t addr;
|
|
|
|
|
|
|
|
addr = ia64_rse_skip_regs (bsp, regs_to_skip);
|
|
|
|
if (locp)
|
|
|
|
*locp = rbs_loc (c->rbs_area + curr, addr);
|
|
|
|
if (rnat_locp)
|
|
|
|
*rnat_locp = rbs_get_rnat_loc (c->rbs_area + curr, addr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (curr == left_edge)
|
|
|
|
{
|
|
|
|
Debug (1, "could not find register r%d!\n", reg);
|
|
|
|
return -UNW_EBADREG;
|
|
|
|
}
|
|
|
|
|
|
|
|
regs_to_skip -= nregs;
|
|
|
|
|
|
|
|
curr = (curr + NELEMS (c->rbs_area) - 1) % NELEMS (c->rbs_area);
|
|
|
|
bsp = c->rbs_area[curr].end - c->rbs_area[curr].size;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-08-31 15:59:10 +02:00
|
|
|
#ifndef REMOTE_ONLY
|
|
|
|
|
2004-08-17 17:34:28 +02:00
|
|
|
static inline int
|
|
|
|
get_rnat (struct cursor *c, struct rbs_area *rbs, unw_word_t bsp,
|
2004-08-31 15:59:10 +02:00
|
|
|
unw_word_t *__restrict rnatp)
|
2004-08-17 17:34:28 +02:00
|
|
|
{
|
2004-08-31 15:59:10 +02:00
|
|
|
ia64_loc_t rnat_locp = rbs_get_rnat_loc (rbs, bsp);
|
|
|
|
|
|
|
|
return ia64_get (c, rnat_locp, rnatp);
|
2004-08-17 17:34:28 +02:00
|
|
|
}
|
|
|
|
|
2004-08-31 15:59:10 +02:00
|
|
|
/* Simulate the effect of "cover" followed by a "flushrs" for the
|
|
|
|
target-frame. However, since the target-frame's backing store
|
|
|
|
may not have space for the registers that got spilled onto other
|
|
|
|
rbs-areas, we save those registers to DIRTY_PARTITION where
|
|
|
|
we can then load them via a single "loadrs".
|
|
|
|
|
|
|
|
This function returns the size of the dirty-partition that was
|
|
|
|
created or a negative error-code in case of error.
|
2004-08-17 17:34:28 +02:00
|
|
|
|
|
|
|
Note: This does not modify the rbs_area[] structure in any way. */
|
|
|
|
HIDDEN int
|
2004-08-31 15:59:10 +02:00
|
|
|
rbs_cover_and_flush (struct cursor *c, unw_word_t nregs,
|
|
|
|
unw_word_t *dirty_partition, unw_word_t *dirty_rnat,
|
|
|
|
unw_word_t *bspstore)
|
2004-08-17 17:34:28 +02:00
|
|
|
{
|
2004-08-31 15:59:10 +02:00
|
|
|
unw_word_t n, src_mask, dst_mask, bsp, *dst, src_rnat, dst_rnat = 0;
|
|
|
|
unw_word_t curr = c->rbs_curr, left_edge = c->rbs_left_edge;
|
|
|
|
struct rbs_area *rbs = c->rbs_area + curr;
|
2004-08-17 17:34:28 +02:00
|
|
|
int ret;
|
|
|
|
|
2004-08-31 15:59:10 +02:00
|
|
|
bsp = c->bsp;
|
|
|
|
c->bsp = ia64_rse_skip_regs (bsp, nregs);
|
2004-08-17 17:34:28 +02:00
|
|
|
|
2004-08-31 15:59:10 +02:00
|
|
|
if (likely (rbs_contains (rbs, bsp)))
|
2004-08-17 17:34:28 +02:00
|
|
|
{
|
2004-08-31 15:59:10 +02:00
|
|
|
/* at least _some_ registers are on rbs... */
|
|
|
|
n = ia64_rse_num_regs (bsp, rbs->end);
|
|
|
|
if (likely (n >= nregs))
|
|
|
|
{
|
|
|
|
/* common case #1: all registers are on current rbs... */
|
|
|
|
/* got lucky: _all_ registers are on rbs... */
|
|
|
|
ia64_loc_t rnat_loc = rbs_get_rnat_loc (rbs, c->bsp);
|
2004-08-17 17:34:28 +02:00
|
|
|
|
2004-08-31 15:59:10 +02:00
|
|
|
*bspstore = c->bsp;
|
2004-08-17 17:34:28 +02:00
|
|
|
|
2004-08-31 15:59:10 +02:00
|
|
|
if (IA64_IS_REG_LOC (rnat_loc))
|
|
|
|
{
|
|
|
|
unw_word_t rnat_addr = (unw_word_t)
|
|
|
|
tdep_uc_addr (c->as_arg, UNW_IA64_AR_RNAT, NULL);
|
|
|
|
rnat_loc = IA64_LOC_ADDR (rnat_addr, 0);
|
|
|
|
}
|
|
|
|
c->loc[IA64_REG_RNAT] = rnat_loc;
|
|
|
|
return 0; /* all done */
|
|
|
|
}
|
|
|
|
nregs -= n; /* account for registers already on the rbs */
|
2004-08-17 17:34:28 +02:00
|
|
|
|
2004-08-31 15:59:10 +02:00
|
|
|
assert (ia64_rse_skip_regs (c->bsp, -nregs) ==
|
|
|
|
ia64_rse_skip_regs (rbs->end, 0));
|
2004-08-17 17:34:28 +02:00
|
|
|
}
|
2004-08-31 15:59:10 +02:00
|
|
|
else
|
|
|
|
/* Earlier frames also didn't get spilled; need to "loadrs" those,
|
|
|
|
too... */
|
|
|
|
nregs += ia64_rse_num_regs (rbs->end, bsp);
|
|
|
|
|
|
|
|
/* OK, we need to copy NREGS registers to the dirty partition. */
|
|
|
|
|
|
|
|
*bspstore = bsp = rbs->end;
|
|
|
|
c->loc[IA64_REG_RNAT] = rbs->rnat_loc;
|
|
|
|
assert (!IA64_IS_REG_LOC (rbs->rnat_loc));
|
2004-08-17 17:34:28 +02:00
|
|
|
|
2004-08-31 15:59:10 +02:00
|
|
|
dst = dirty_partition;
|
|
|
|
|
|
|
|
while (nregs > 0)
|
2004-08-17 17:34:28 +02:00
|
|
|
{
|
2004-08-31 15:59:10 +02:00
|
|
|
if (unlikely (!rbs_contains (rbs, bsp)))
|
2004-08-17 17:34:28 +02:00
|
|
|
{
|
2004-08-31 15:59:10 +02:00
|
|
|
/* switch to next non-empty rbs-area: */
|
|
|
|
do
|
2004-08-17 17:34:28 +02:00
|
|
|
{
|
2004-08-31 15:59:10 +02:00
|
|
|
if (curr == left_edge)
|
|
|
|
{
|
|
|
|
Debug (0, "rbs-underflow while flushing %lu regs, "
|
|
|
|
"bsp=0x%lx, dst=0x%p\n", (unsigned long) nregs,
|
|
|
|
(unsigned long) bsp, dst);
|
|
|
|
return -UNW_EBADREG;
|
|
|
|
}
|
|
|
|
|
|
|
|
assert (ia64_rse_num_regs (rbs->end, bsp) == 0);
|
|
|
|
|
|
|
|
curr = (curr + NELEMS (c->rbs_area) - 1) % NELEMS (c->rbs_area);
|
|
|
|
rbs = c->rbs_area + curr;
|
|
|
|
bsp = rbs->end - rbs->size;
|
2004-08-17 17:34:28 +02:00
|
|
|
}
|
2004-08-31 15:59:10 +02:00
|
|
|
while (rbs->size == 0);
|
2004-08-17 17:34:28 +02:00
|
|
|
|
2004-08-31 15:59:10 +02:00
|
|
|
if ((ret = get_rnat (c, rbs, bsp, &src_rnat)) < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
2004-08-17 17:34:28 +02:00
|
|
|
|
2004-08-31 15:59:10 +02:00
|
|
|
if (unlikely (ia64_rse_is_rnat_slot (bsp)))
|
|
|
|
{
|
|
|
|
bsp += 8;
|
|
|
|
if ((ret = get_rnat (c, rbs, bsp, &src_rnat)) < 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
if (unlikely (ia64_rse_is_rnat_slot ((unw_word_t) dst)))
|
|
|
|
{
|
|
|
|
*dst++ = dst_rnat;
|
|
|
|
dst_rnat = 0;
|
|
|
|
}
|
2004-08-17 17:34:28 +02:00
|
|
|
|
2004-08-31 15:59:10 +02:00
|
|
|
src_mask = ((unw_word_t) 1) << ia64_rse_slot_num (bsp);
|
|
|
|
dst_mask = ((unw_word_t) 1) << ia64_rse_slot_num ((unw_word_t) dst);
|
2004-08-17 17:34:28 +02:00
|
|
|
|
|
|
|
if (src_rnat & src_mask)
|
|
|
|
dst_rnat |= dst_mask;
|
|
|
|
else
|
|
|
|
dst_rnat &= ~dst_mask;
|
|
|
|
|
2004-08-31 15:59:10 +02:00
|
|
|
/* copy one slot: */
|
|
|
|
if ((ret = ia64_get (c, rbs_loc (rbs, bsp), dst)) < 0)
|
2004-08-17 17:34:28 +02:00
|
|
|
return ret;
|
|
|
|
|
2004-08-31 15:59:10 +02:00
|
|
|
/* advance to next slot: */
|
|
|
|
--nregs;
|
|
|
|
bsp += 8;
|
|
|
|
++dst;
|
|
|
|
}
|
|
|
|
if (unlikely (ia64_rse_is_rnat_slot ((unw_word_t) dst)))
|
|
|
|
{
|
|
|
|
/* The LOADRS instruction loads "the N bytes below the current
|
|
|
|
BSP" but BSP can never point to an RNaT slot so if the last
|
|
|
|
destination word happens to be an RNaT slot, we need to write
|
|
|
|
that slot now. */
|
|
|
|
*dst++ = dst_rnat;
|
|
|
|
dst_rnat = 0;
|
2004-08-17 17:34:28 +02:00
|
|
|
}
|
2004-08-31 15:59:10 +02:00
|
|
|
*dirty_rnat = dst_rnat;
|
|
|
|
return (char *) dst - (char *) dirty_partition;
|
2004-08-17 17:34:28 +02:00
|
|
|
}
|
2004-08-31 15:59:10 +02:00
|
|
|
|
|
|
|
#endif /* !REMOTE_ONLY */
|