/* libunwind - a platform-independent unwind library
Copyright (C) 2001-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. */
#include "unwind_i.h"
/* forward declaration: */
static int create_state_record_for (struct cursor *c,
struct ia64_state_record *sr,
unw_word_t ip);
typedef unsigned long unw_word;
#define alloc_reg_state() (mempool_alloc (&unw.reg_state_pool))
#define free_reg_state(rs) (mempool_free (&unw.reg_state_pool, rs))
#define alloc_labeled_state() (mempool_alloc (&unw.labeled_state_pool))
#define free_labeled_state(s) (mempool_free (&unw.labeled_state_pool, s))
/* Routines to manipulate the state stack. */
static inline void
push (struct ia64_state_record *sr)
{
struct ia64_reg_state *rs;
rs = alloc_reg_state ();
if (!rs)
{
print_error ("libunwind: cannot stack reg state!\n");
return;
}
memcpy (rs, &sr->curr, sizeof (*rs));
sr->curr.next = rs;
}
static void
pop (struct ia64_state_record *sr)
{
struct ia64_reg_state *rs = sr->curr.next;
if (!rs)
{
print_error ("libunwind: stack underflow!\n");
return;
}
memcpy (&sr->curr, rs, sizeof (*rs));
free_reg_state (rs);
}
/* Make a copy of the state stack. Non-recursive to avoid stack overflows. */
static struct ia64_reg_state *
dup_state_stack (struct ia64_reg_state *rs)
{
struct ia64_reg_state *copy, *prev = NULL, *first = NULL;
while (rs)
{
copy = alloc_reg_state ();
if (!copy)
{
print_error ("unwind.dup_state_stack: out of memory\n");
return NULL;
}
memcpy (copy, rs, sizeof (*copy));
if (first)
prev->next = copy;
else
first = copy;
rs = rs->next;
prev = copy;
}
return first;
}
/* Free all stacked register states (but not RS itself). */
static void
free_state_stack (struct ia64_reg_state *rs)
{
struct ia64_reg_state *p, *next;
for (p = rs->next; p != NULL; p = next)
{
next = p->next;
free_reg_state (p);
}
rs->next = NULL;
}
/* Unwind decoder routines */
static enum ia64_pregnum CONST_ATTR
decode_abreg (unsigned char abreg, int memory)
{
switch (abreg)
{
case 0x04 ... 0x07:
return IA64_REG_R4 + (abreg - 0x04);
case 0x22 ... 0x25:
return IA64_REG_F2 + (abreg - 0x22);
case 0x30 ... 0x3f:
return IA64_REG_F16 + (abreg - 0x30);
case 0x41 ... 0x45:
return IA64_REG_B1 + (abreg - 0x41);
case 0x60:
return IA64_REG_PR;
case 0x61:
return IA64_REG_PSP;
case 0x62:
return memory ? IA64_REG_PRI_UNAT_MEM : IA64_REG_PRI_UNAT_GR;
case 0x63:
return IA64_REG_IP;
case 0x64:
return IA64_REG_BSP;
case 0x65:
return IA64_REG_BSPSTORE;
case 0x66:
return IA64_REG_RNAT;
case 0x67:
return IA64_REG_UNAT;
case 0x68:
return IA64_REG_FPSR;
case 0x69:
return IA64_REG_PFS;
case 0x6a:
return IA64_REG_LC;
default:
break;
}
Dprintf ("libunwind: bad abreg=0x%x\n", abreg);
return IA64_REG_LC;
}
static void
set_reg (struct ia64_reg_info *reg, enum ia64_where where, int when,
unsigned long val)
{
reg->val = val;
reg->where = where;
if (reg->when == IA64_WHEN_NEVER)
reg->when = when;
}
static void
alloc_spill_area (unsigned long *offp, unsigned long regsize,
struct ia64_reg_info *lo, struct ia64_reg_info *hi)
{
struct ia64_reg_info *reg;
for (reg = hi; reg >= lo; --reg)
{
if (reg->where == IA64_WHERE_SPILL_HOME)
{
reg->where = IA64_WHERE_PSPREL;
*offp -= regsize;
reg->val = *offp;
}
}
}
static inline void
spill_next_when (struct ia64_reg_info **regp, struct ia64_reg_info *lim,
unw_word t)
{
struct ia64_reg_info *reg;
for (reg = *regp; reg <= lim; ++reg)
{
if (reg->where == IA64_WHERE_SPILL_HOME)
{
reg->when = t;
*regp = reg + 1;
return;
}
}
Dprintf ("libunwind: excess spill!\n");
}
static inline void
finish_prologue (struct ia64_state_record *sr)
{
struct ia64_reg_info *reg;
unsigned long off;
int i;
/* First, resolve implicit register save locations (see Section
"11.4.2.3 Rules for Using Unwind Descriptors", rule 3). */
for (i = 0; i < (int) ARRAY_SIZE (unw.save_order); ++i)
{
reg = sr->curr.reg + unw.save_order[i];
if (reg->where == IA64_WHERE_GR_SAVE)
{
reg->where = IA64_WHERE_GR;
reg->val = sr->gr_save_loc++;
}
}
/* Next, compute when the fp, general, and branch registers get
saved. This must come before alloc_spill_area() because we need
to know which registers are spilled to their home locations. */
if (sr->imask)
{
unsigned char kind, mask = 0, *cp = sr->imask;
unsigned long t;
static const unsigned char limit[3] =
{
IA64_REG_F31, IA64_REG_R7, IA64_REG_B5
};
struct ia64_reg_info *(regs[3]);
regs[0] = sr->curr.reg + IA64_REG_F2;
regs[1] = sr->curr.reg + IA64_REG_R4;
regs[2] = sr->curr.reg + IA64_REG_B1;
for (t = 0; (int) t < sr->region_len; ++t)
{
if ((t & 3) == 0)
mask = *cp++;
kind = (mask >> 2 * (3 - (t & 3))) & 3;
if (kind > 0)
spill_next_when (®s[kind - 1], sr->curr.reg + limit[kind - 1],
sr->region_start + t);
}
}
/* Next, lay out the memory stack spill area. */
if (sr->any_spills)
{
off = sr->spill_offset;
alloc_spill_area (&off, 16, sr->curr.reg + IA64_REG_F2,
sr->curr.reg + IA64_REG_F31);
alloc_spill_area (&off, 8, sr->curr.reg + IA64_REG_B1,
sr->curr.reg + IA64_REG_B5);
alloc_spill_area (&off, 8, sr->curr.reg + IA64_REG_R4,
sr->curr.reg + IA64_REG_R7);
}
}
/* Region header descriptors. */
static void
desc_prologue (int body, unw_word rlen, unsigned char mask,
unsigned char grsave, struct ia64_state_record *sr)
{
int i, region_start;
if (!(sr->in_body || sr->first_region))
finish_prologue (sr);
sr->first_region = 0;
/* check if we're done: */
if (sr->when_target < sr->region_start + sr->region_len)
{
sr->done = 1;
return;
}
region_start = sr->region_start + sr->region_len;
for (i = 0; i < sr->epilogue_count; ++i)
pop (sr);
sr->epilogue_count = 0;
sr->when_sp_restored = IA64_WHEN_NEVER;
sr->region_start = region_start;
sr->region_len = rlen;
sr->in_body = body;
if (!body)
{
push (sr);
if (mask)
for (i = 0; i < 4; ++i)
{
if (mask & 0x8)
set_reg (sr->curr.reg + unw.save_order[i],