123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- /* libunwind - a platform-independent unwind library
- Copyright (C) 2003-2005 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"
- #if UNW_DEBUG
- HIDDEN const char *
- ia64_strloc (ia64_loc_t loc)
- {
- static char buf[128];
- if (IA64_IS_NULL_LOC (loc))
- return "<null>";
- buf[0] = '\0';
- if (IA64_IS_MEMSTK_NAT (loc))
- strcat (buf, "memstk_nat(");
- if (IA64_IS_UC_LOC (loc))
- strcat (buf, "uc(");
- if (IA64_IS_FP_LOC (loc))
- strcat (buf, "fp(");
- if (IA64_IS_REG_LOC (loc))
- sprintf (buf + strlen (buf), "%s", unw_regname (IA64_GET_REG (loc)));
- else
- sprintf (buf + strlen (buf), "0x%llx",
- (unsigned long long) IA64_GET_ADDR (loc));
- if (IA64_IS_FP_LOC (loc))
- strcat (buf, ")");
- if (IA64_IS_UC_LOC (loc))
- strcat (buf, ")");
- if (IA64_IS_MEMSTK_NAT (loc))
- strcat (buf, ")");
- return buf;
- }
- #endif /* UNW_DEBUG */
- 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, rbs_base;
- int ret;
- Debug (10, "(left=%u, curr=%u)\n", c->rbs_left_edge, c->rbs_curr);
- /* Calculate address "lo" at which the backing store starts: */
- ndirty = rse_num_regs (saved_bspstore, saved_bsp);
- lo = 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)
- {
- Debug (10, "inner=[0x%lx-0x%lx)\n",
- (long) (rbs->end - rbs->size), (long) rbs->end);
- c->rbs_curr = (c->rbs_curr + 1) % ARRAY_SIZE (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) % ARRAY_SIZE (c->rbs_area);
- }
- if ((ret = rbs_get_base (c, saved_bspstore, &rbs_base)) < 0)
- return ret;
- rbs->end = saved_bspstore;
- rbs->size = saved_bspstore - rbs_base;
- rbs->rnat_loc = saved_rnat_loc;
- c->bsp = saved_bsp;
- Debug (10, "outer=[0x%llx-0x%llx), rnat@%s\n", (long long) rbs_base,
- (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 = rse_num_regs (c->rbs_area[curr].end, bsp);
- curr = (curr + ARRAY_SIZE (c->rbs_area) - 1) % ARRAY_SIZE (c->rbs_area);
- bsp = rse_skip_regs (c->rbs_area[curr].end - c->rbs_area[curr].size, n);
- }
- while (1)
- {
- nregs = rse_num_regs (bsp, c->rbs_area[curr].end);
- if (regs_to_skip < nregs)
- {
- /* found it: */
- unw_word_t addr;
- addr = 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 + ARRAY_SIZE (c->rbs_area) - 1) % ARRAY_SIZE (c->rbs_area);
- bsp = c->rbs_area[curr].end - c->rbs_area[curr].size;
- }
- }
- #ifdef NEED_RBS_COVER_AND_FLUSH
- static inline int
- get_rnat (struct cursor *c, struct rbs_area *rbs, unw_word_t bsp,
- unw_word_t *__restrict rnatp)
- {
- ia64_loc_t rnat_locp = rbs_get_rnat_loc (rbs, bsp);
- return ia64_get (c, rnat_locp, rnatp);
- }
- /* 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.
- Note: This does not modify the rbs_area[] structure in any way. */
- HIDDEN int
- 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)
- {
- 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;
- int ret;
- bsp = c->bsp;
- c->bsp = rse_skip_regs (bsp, nregs);
- if (likely (rbs_contains (rbs, bsp)))
- {
- /* at least _some_ registers are on rbs... */
- n = 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);
- *bspstore = c->bsp;
- 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 */
- assert (rse_skip_regs (c->bsp, -nregs) == rse_skip_regs (rbs->end, 0));
- }
- else
- /* Earlier frames also didn't get spilled; need to "loadrs" those,
- too... */
- nregs += 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));
- dst = dirty_partition;
- while (nregs > 0)
- {
- if (unlikely (!rbs_contains (rbs, bsp)))
- {
- /* switch to next non-empty rbs-area: */
- do
- {
- 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 (rse_num_regs (rbs->end, bsp) == 0);
- curr = (curr + ARRAY_SIZE (c->rbs_area) - 1)
- % ARRAY_SIZE (c->rbs_area);
- rbs = c->rbs_area + curr;
- bsp = rbs->end - rbs->size;
- }
- while (rbs->size == 0);
- if ((ret = get_rnat (c, rbs, bsp, &src_rnat)) < 0)
- return ret;
- }
- if (unlikely (rse_is_rnat_slot (bsp)))
- {
- bsp += 8;
- if ((ret = get_rnat (c, rbs, bsp, &src_rnat)) < 0)
- return ret;
- }
- if (unlikely (rse_is_rnat_slot ((unw_word_t) dst)))
- {
- *dst++ = dst_rnat;
- dst_rnat = 0;
- }
- src_mask = ((unw_word_t) 1) << rse_slot_num (bsp);
- dst_mask = ((unw_word_t) 1) << rse_slot_num ((unw_word_t) dst);
- if (src_rnat & src_mask)
- dst_rnat |= dst_mask;
- else
- dst_rnat &= ~dst_mask;
- /* copy one slot: */
- if ((ret = ia64_get (c, rbs_loc (rbs, bsp), dst)) < 0)
- return ret;
- /* advance to next slot: */
- --nregs;
- bsp += 8;
- ++dst;
- }
- if (unlikely (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;
- }
- *dirty_rnat = dst_rnat;
- return (char *) dst - (char *) dirty_partition;
- }
- #endif /* !UNW_REMOTE_ONLY */
|