diff --git a/src/ia64/Gparser-ia64.c b/src/ia64/Gparser-ia64.c index e69de29b..2b6b0fe4 100644 --- a/src/ia64/Gparser-ia64.c +++ b/src/ia64/Gparser-ia64.c @@ -0,0 +1,1057 @@ +/* libunwind - a platform-independent unwind library + Copyright (C) 2001-2002 Hewlett-Packard Co + Contributed by David Mosberger-Tang + +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 +#include +#include +#include + +#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.state_record_pool)) +#define free_reg_state(rs) (mempool_free (&unw.state_record_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) + { + fprintf (stderr, "unwind: 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) + { + fprintf (stderr, "unwind: 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) + { + fprintf (stderr, "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 __attribute__ ((const)) +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_RP; + 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 ("unwind: 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 ("unwind: 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) sizeof (unw.save_order) / sizeof (unw.save_order[0]); + ++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; 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; + + 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; + } + + for (i = 0; i < sr->epilogue_count; ++i) + pop (sr); + sr->epilogue_count = 0; + sr->epilogue_start = IA64_WHEN_NEVER; + + if (!body) + push (sr); + + sr->region_start += sr->region_len; + sr->region_len = rlen; + sr->in_body = body; + + if (!body) + { + if (mask) + for (i = 0; i < 4; ++i) + { + if (mask & 0x8) + set_reg (sr->curr.reg + unw.save_order[i], IA64_WHERE_GR, + sr->region_start + sr->region_len - 1, grsave++); + mask <<= 1; + } + sr->gr_save_loc = grsave; + sr->any_spills = 0; + sr->imask = 0; + sr->spill_offset = 0x10; /* default to psp+16 */ + } +} + +/* Prologue descriptors. */ + +static inline void +desc_abi (unsigned char abi, unsigned char context, + struct ia64_state_record *sr) +{ + if (abi == 0 && context == 's') + sr->is_signal_frame = 1; + else + dprintf ("unwind: ignoring unwabi(abi=0x%x,context=0x%x)\n", abi, context); +} + +static inline void +desc_br_gr (unsigned char brmask, unsigned char gr, + struct ia64_state_record *sr) +{ + int i; + + for (i = 0; i < 5; ++i) + { + if (brmask & 1) + set_reg (sr->curr.reg + IA64_REG_B1 + i, IA64_WHERE_GR, + sr->region_start + sr->region_len - 1, gr++); + brmask >>= 1; + } +} + +static inline void +desc_br_mem (unsigned char brmask, struct ia64_state_record *sr) +{ + int i; + + for (i = 0; i < 5; ++i) + { + if (brmask & 1) + { + set_reg (sr->curr.reg + IA64_REG_B1 + i, IA64_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + brmask >>= 1; + } +} + +static inline void +desc_frgr_mem (unsigned char grmask, unw_word frmask, + struct ia64_state_record *sr) +{ + int i; + + for (i = 0; i < 4; ++i) + { + if ((grmask & 1) != 0) + { + set_reg (sr->curr.reg + IA64_REG_R4 + i, IA64_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + grmask >>= 1; + } + for (i = 0; i < 20; ++i) + { + if ((frmask & 1) != 0) + { + int base = (i < 4) ? IA64_REG_F2 : IA64_REG_F16 - 4; + set_reg (sr->curr.reg + base + i, IA64_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + frmask >>= 1; + } +} + +static inline void +desc_fr_mem (unsigned char frmask, struct ia64_state_record *sr) +{ + int i; + + for (i = 0; i < 4; ++i) + { + if ((frmask & 1) != 0) + { + set_reg (sr->curr.reg + IA64_REG_F2 + i, IA64_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + frmask >>= 1; + } +} + +static inline void +desc_gr_gr (unsigned char grmask, unsigned char gr, + struct ia64_state_record *sr) +{ + int i; + + for (i = 0; i < 4; ++i) + { + if ((grmask & 1) != 0) + set_reg (sr->curr.reg + IA64_REG_R4 + i, IA64_WHERE_GR, + sr->region_start + sr->region_len - 1, gr++); + grmask >>= 1; + } +} + +static inline void +desc_gr_mem (unsigned char grmask, struct ia64_state_record *sr) +{ + int i; + + for (i = 0; i < 4; ++i) + { + if ((grmask & 1) != 0) + { + set_reg (sr->curr.reg + IA64_REG_R4 + i, IA64_WHERE_SPILL_HOME, + sr->region_start + sr->region_len - 1, 0); + sr->any_spills = 1; + } + grmask >>= 1; + } +} + +static inline void +desc_mem_stack_f (unw_word t, unw_word size, struct ia64_state_record *sr) +{ + set_reg (sr->curr.reg + IA64_REG_PSP, IA64_WHERE_NONE, + sr->region_start + MIN ((int) t, sr->region_len - 1), 16 * size); +} + +static inline void +desc_mem_stack_v (unw_word t, struct ia64_state_record *sr) +{ + sr->curr.reg[IA64_REG_PSP].when = + sr->region_start + MIN ((int) t, sr->region_len - 1); +} + +static inline void +desc_reg_gr (unsigned char reg, unsigned char dst, + struct ia64_state_record *sr) +{ + set_reg (sr->curr.reg + reg, IA64_WHERE_GR, + sr->region_start + sr->region_len - 1, dst); +} + +static inline void +desc_reg_psprel (unsigned char reg, unw_word pspoff, + struct ia64_state_record *sr) +{ + set_reg (sr->curr.reg + reg, IA64_WHERE_PSPREL, + sr->region_start + sr->region_len - 1, 0x10 - 4 * pspoff); +} + +static inline void +desc_reg_sprel (unsigned char reg, unw_word spoff, + struct ia64_state_record *sr) +{ + set_reg (sr->curr.reg + reg, IA64_WHERE_SPREL, + sr->region_start + sr->region_len - 1, 4 * spoff); +} + +static inline void +desc_rp_br (unsigned char dst, struct ia64_state_record *sr) +{ + sr->return_link_reg = dst; +} + +static inline void +desc_reg_when (unsigned char regnum, unw_word t, struct ia64_state_record *sr) +{ + struct ia64_reg_info *reg = sr->curr.reg + regnum; + + if (reg->where == IA64_WHERE_NONE) + reg->where = IA64_WHERE_GR_SAVE; + reg->when = sr->region_start + MIN ((int) t, sr->region_len - 1); +} + +static inline void +desc_spill_base (unw_word pspoff, struct ia64_state_record *sr) +{ + sr->spill_offset = 0x10 - 4 * pspoff; +} + +static inline unsigned char * +desc_spill_mask (unsigned char *imaskp, struct ia64_state_record *sr) +{ + sr->imask = imaskp; + return imaskp + (2 * sr->region_len + 7) / 8; +} + +/* Body descriptors. */ + +static inline void +desc_epilogue (unw_word t, unw_word ecount, struct ia64_state_record *sr) +{ + sr->epilogue_start = sr->region_start + sr->region_len - 1 - t; + sr->epilogue_count = ecount + 1; +} + +static inline void +desc_copy_state (unw_word label, struct ia64_state_record *sr) +{ + struct ia64_labeled_state *ls; + + for (ls = sr->labeled_states; ls; ls = ls->next) + { + if (ls->label == label) + { + free_state_stack (&sr->curr); + memcpy (&sr->curr, &ls->saved_state, sizeof (sr->curr)); + sr->curr.next = dup_state_stack (ls->saved_state.next); + return; + } + } + fprintf (stderr, "unwind: failed to find state labeled 0x%lx\n", label); +} + +static inline void +desc_label_state (unw_word label, struct ia64_state_record *sr) +{ + struct ia64_labeled_state *ls; + + ls = alloc_labeled_state (); + if (!ls) + { + fprintf (stderr, "unwind.desc_label_state(): out of memory\n"); + return; + } + ls->label = label; + memcpy (&ls->saved_state, &sr->curr, sizeof (ls->saved_state)); + ls->saved_state.next = dup_state_stack (sr->curr.next); + + /* insert into list of labeled states: */ + ls->next = sr->labeled_states; + sr->labeled_states = ls; +} + +/* General descriptors. */ + +static inline int +desc_is_active (unsigned char qp, unw_word t, struct ia64_state_record *sr) +{ + if (sr->when_target <= sr->region_start + MIN ((int) t, sr->region_len - 1)) + return 0; + if (qp > 0) + { + if ((sr->pr_val & (1UL << qp)) == 0) + return 0; + sr->pr_mask |= (1UL << qp); + } + return 1; +} + +static inline void +desc_restore_p (unsigned char qp, unw_word t, unsigned char abreg, + struct ia64_state_record *sr) +{ + struct ia64_reg_info *r; + + if (!desc_is_active (qp, t, sr)) + return; + + r = sr->curr.reg + decode_abreg (abreg, 0); + r->where = IA64_WHERE_NONE; + r->when = IA64_WHEN_NEVER; + r->val = 0; +} + +static inline void +desc_spill_reg_p (unsigned char qp, unw_word t, unsigned char abreg, + unsigned char x, unsigned char ytreg, + struct ia64_state_record *sr) +{ + enum ia64_where where = IA64_WHERE_GR; + struct ia64_reg_info *r; + + if (!desc_is_active (qp, t, sr)) + return; + + if (x) + where = IA64_WHERE_BR; + else if (ytreg & 0x80) + where = IA64_WHERE_FR; + + r = sr->curr.reg + decode_abreg (abreg, 0); + r->where = where; + r->when = sr->region_start + MIN ((int) t, sr->region_len - 1); + r->val = (ytreg & 0x7f); +} + +static inline void +desc_spill_psprel_p (unsigned char qp, unw_word t, unsigned char abreg, + unw_word pspoff, struct ia64_state_record *sr) +{ + struct ia64_reg_info *r; + + if (!desc_is_active (qp, t, sr)) + return; + + r = sr->curr.reg + decode_abreg (abreg, 1); + r->where = IA64_WHERE_PSPREL; + r->when = sr->region_start + MIN ((int) t, sr->region_len - 1); + r->val = 0x10 - 4 * pspoff; +} + +static inline void +desc_spill_sprel_p (unsigned char qp, unw_word t, unsigned char abreg, + unw_word spoff, struct ia64_state_record *sr) +{ + struct ia64_reg_info *r; + + if (!desc_is_active (qp, t, sr)) + return; + + r = sr->curr.reg + decode_abreg (abreg, 1); + r->where = IA64_WHERE_SPREL; + r->when = sr->region_start + MIN ((int) t, sr->region_len - 1); + r->val = 4 * spoff; +} + +#define UNW_DEC_BAD_CODE(code) fprintf (stderr, "unwind: unknown code 0x%02x\n", code); + +/* Register names. */ +#define UNW_REG_BSP IA64_REG_BSP +#define UNW_REG_BSPSTORE IA64_REG_BSPSTORE +#define UNW_REG_FPSR IA64_REG_FPSR +#define UNW_REG_LC IA64_REG_LC +#define UNW_REG_PFS IA64_REG_PFS +#define UNW_REG_PR IA64_REG_PR +#define UNW_REG_RNAT IA64_REG_RNAT +#define UNW_REG_PSP IA64_REG_PSP +#define UNW_REG_RP IA64_REG_RP +#define UNW_REG_UNAT IA64_REG_UNAT + +/* Region headers. */ +#define UNW_DEC_PROLOGUE_GR(fmt,r,m,gr,arg) desc_prologue(0,r,m,gr,arg) +#define UNW_DEC_PROLOGUE(fmt,b,r,arg) desc_prologue(b,r,0,32,arg) + +/* Prologue descriptors. */ +#define UNW_DEC_ABI(fmt,a,c,arg) desc_abi(a,c,arg) +#define UNW_DEC_BR_GR(fmt,b,g,arg) desc_br_gr(b,g,arg) +#define UNW_DEC_BR_MEM(fmt,b,arg) desc_br_mem(b,arg) +#define UNW_DEC_FRGR_MEM(fmt,g,f,arg) desc_frgr_mem(g,f,arg) +#define UNW_DEC_FR_MEM(fmt,f,arg) desc_fr_mem(f,arg) +#define UNW_DEC_GR_GR(fmt,m,g,arg) desc_gr_gr(m,g,arg) +#define UNW_DEC_GR_MEM(fmt,m,arg) desc_gr_mem(m,arg) +#define UNW_DEC_MEM_STACK_F(fmt,t,s,arg) desc_mem_stack_f(t,s,arg) +#define UNW_DEC_MEM_STACK_V(fmt,t,arg) desc_mem_stack_v(t,arg) +#define UNW_DEC_REG_GR(fmt,r,d,arg) desc_reg_gr(r,d,arg) +#define UNW_DEC_REG_PSPREL(fmt,r,o,arg) desc_reg_psprel(r,o,arg) +#define UNW_DEC_REG_SPREL(fmt,r,o,arg) desc_reg_sprel(r,o,arg) +#define UNW_DEC_REG_WHEN(fmt,r,t,arg) desc_reg_when(r,t,arg) +#define UNW_DEC_PRIUNAT_WHEN_GR(fmt,t,arg) \ + desc_reg_when(IA64_REG_PRI_UNAT_GR,t,arg) +#define UNW_DEC_PRIUNAT_WHEN_MEM(fmt,t,arg) \ + desc_reg_when(IA64_REG_PRI_UNAT_MEM,t,arg) +#define UNW_DEC_PRIUNAT_GR(fmt,r,arg) \ + desc_reg_gr(IA64_REG_PRI_UNAT_GR,r,arg) +#define UNW_DEC_PRIUNAT_PSPREL(fmt,o,arg) \ + desc_reg_psprel(IA64_REG_PRI_UNAT_MEM,o,arg) +#define UNW_DEC_PRIUNAT_SPREL(fmt,o,arg) \ + desc_reg_sprel(IA64_REG_PRI_UNAT_MEM,o,arg) +#define UNW_DEC_RP_BR(fmt,d,arg) desc_rp_br(d,arg) +#define UNW_DEC_SPILL_BASE(fmt,o,arg) desc_spill_base(o,arg) +#define UNW_DEC_SPILL_MASK(fmt,m,arg) (m = desc_spill_mask(m,arg)) + +/* Body descriptors. */ +#define UNW_DEC_EPILOGUE(fmt,t,c,arg) desc_epilogue(t,c,arg) +#define UNW_DEC_COPY_STATE(fmt,l,arg) desc_copy_state(l,arg) +#define UNW_DEC_LABEL_STATE(fmt,l,arg) desc_label_state(l,arg) + +/* General unwind descriptors. */ +#define UNW_DEC_SPILL_REG_P(f,p,t,a,x,y,arg) desc_spill_reg_p(p,t,a,x,y,arg) +#define UNW_DEC_SPILL_REG(f,t,a,x,y,arg) desc_spill_reg_p(0,t,a,x,y,arg) +#define UNW_DEC_SPILL_PSPREL_P(f,p,t,a,o,arg) \ + desc_spill_psprel_p(p,t,a,o,arg) +#define UNW_DEC_SPILL_PSPREL(f,t,a,o,arg) \ + desc_spill_psprel_p(0,t,a,o,arg) +#define UNW_DEC_SPILL_SPREL_P(f,p,t,a,o,arg) desc_spill_sprel_p(p,t,a,o,arg) +#define UNW_DEC_SPILL_SPREL(f,t,a,o,arg) desc_spill_sprel_p(0,t,a,o,arg) +#define UNW_DEC_RESTORE_P(f,p,t,a,arg) desc_restore_p(p,t,a,arg) +#define UNW_DEC_RESTORE(f,t,a,arg) desc_restore_p(0,t,a,arg) + +#include "unwind_decoder.h" + +/* parse dynamic unwind info */ + +static struct ia64_reg_info * +lookup_preg (int regnum, int memory, struct ia64_state_record *sr) +{ + int preg; + + switch (regnum) + { + case UNW_IA64_AR_BSP: preg = IA64_REG_BSP; break; + case UNW_IA64_AR_BSPSTORE: preg = IA64_REG_BSPSTORE; break; + case UNW_IA64_AR_FPSR: preg = IA64_REG_FPSR; break; + case UNW_IA64_AR_LC: preg = IA64_REG_LC; break; + case UNW_IA64_AR_PFS: preg = IA64_REG_PFS; break; + case UNW_IA64_AR_RNAT: preg = IA64_REG_RNAT; break; + case UNW_IA64_AR_UNAT: preg = IA64_REG_UNAT; break; + case UNW_IA64_BR + 0: preg = IA64_REG_RP; break; + case UNW_IA64_PR: preg = IA64_REG_PR; break; + case UNW_IA64_SP: preg = IA64_REG_PSP; break; + + case UNW_IA64_NAT: + if (memory) + preg = IA64_REG_PRI_UNAT_MEM; + else + preg = IA64_REG_PRI_UNAT_GR; + break; + + case UNW_IA64_GR + 4 ... UNW_IA64_GR + 7: + preg = IA64_REG_R4 + (regnum - (UNW_IA64_GR + 4)); + break; + + case UNW_IA64_BR + 1 ... UNW_IA64_BR + 5: + preg = IA64_REG_B1 + (regnum - UNW_IA64_BR); + break; + + case UNW_IA64_FR + 2 ... UNW_IA64_FR + 5: + preg = IA64_REG_F2 + (regnum - (UNW_IA64_FR + 2)); + break; + + case UNW_IA64_FR + 16 ... UNW_IA64_FR + 31: + preg = IA64_REG_F16 + (regnum - (UNW_IA64_FR + 16)); + break; + + default: + dprintf ("unwind.%s: invalid register number %d\n", + __FUNCTION__, regnum); + return NULL; + } + return sr->curr.reg + preg; +} + +static int +parse_dynamic (struct cursor *c, struct ia64_state_record *sr) +{ + unw_dyn_info_t *di = c->pi.unwind_info; + unw_dyn_proc_info_t *proc = &di->u.pi; + unw_dyn_region_info_t *r; + struct ia64_reg_info *ri; + unw_word_t val, new_ip; + enum ia64_where where; + unw_dyn_op_t *op; + int32_t when; + int8_t qp; + int memory; + + for (r = proc->regions; r; r = r->next) + { + /* all regions are treated as prologue regions: */ + desc_prologue (0, r->insn_count, 0, 0, sr); + + for (op = r->op; 1; ++op) + { + when = op->when; + val = op->val; + qp = op->qp; + + if (!desc_is_active (qp, when, sr)) + continue; + + switch (op->tag) + { + case UNW_DYN_SAVE_REG: + memory = 0; + if ((unsigned) (val - UNW_IA64_GR) < 128) + where = IA64_WHERE_GR; + else if ((unsigned) (val - UNW_IA64_FR) < 128) + where = IA64_WHERE_FR; + else if ((unsigned) (val - UNW_IA64_BR) < 8) + where = IA64_WHERE_BR; + else + { + dprintf ("unwind.%s: can't save to register number %d\n", + __FUNCTION__, (int) op->reg); + return -UNW_EBADREG; + } + /* fall through */ + update_reg_info: + ri = lookup_preg (op->reg, memory, sr); + if (!ri) + return -UNW_EBADREG; + ri->where = where; + ri->when = when; + ri->val = val; + break; + + case UNW_DYN_SPILL_FP_REL: + memory = 1; + where = IA64_WHERE_PSPREL; + goto update_reg_info; + + case UNW_DYN_SPILL_SP_REL: + memory = 1; + where = IA64_WHERE_SPREL; + goto update_reg_info; + + case UNW_DYN_ADD: + if (op->reg == UNW_IA64_SP) + { + if (val & 0xf) + { + dprintf ("unwind.%s: frame-size %ld not an integer " + "multiple of 16\n", + __FUNCTION__, (long) op->val); + return -UNW_EINVAL; + } + } + else + { + dprintf ("unwind.%s: can only ADD to stack-pointer\n", + __FUNCTION__); + return -UNW_EBADREG; + } + break; + + case UNW_DYN_POP_FRAMES: + sr->epilogue_start + = sr->region_start + sr->region_len - 1 - op->when; + sr->epilogue_count = op->val; + break; + + case UNW_DYN_LABEL_STATE: + desc_label_state(op->val, sr); + break; + + case UNW_DYN_COPY_STATE: + desc_copy_state(op->val, sr); + break; + + case UNW_DYN_ALIAS: + while (sr->curr.next) + pop (sr); + new_ip = op->val + ((sr->when_target / 3) * 16 + + (sr->when_target % 3)); + return create_state_record_for (c, sr, new_ip); + + case UNW_DYN_STOP: + goto end_of_ops; + } + } + } + while (!sr->done); + + end_of_ops: + return 0; +} + + +static int +get_proc_info (struct cursor *c, unw_word_t ip, int need_unwind_info) +{ + int ret, dynamic = 1; + + if (c->pi_valid && !need_unwind_info) + return 0; + + /* check dynamic info first --- it overrides everything else */ + ret = unwi_find_dynamic_proc_info (c->as, ip, &c->pi, need_unwind_info, + c->as_arg); + if (ret == -UNW_ENOINFO) + { + dynamic = 0; + ret = ia64_find_proc_info (c, ip, need_unwind_info); + } + + c->pi_valid = 1; + c->pi_is_dynamic = dynamic; + return ret; +} + +static inline void +put_unwind_info (struct cursor *c, unw_proc_info_t *pi) +{ + if (!c->pi_valid) + return; + + if ((c)->pi_is_dynamic) + unwi_put_dynamic_unwind_info (c->as, pi, c->as_arg); + else + ia64_put_unwind_info(c, pi); +} + +static int +create_state_record_for (struct cursor *c, struct ia64_state_record *sr, + unw_word_t ip) +{ + unw_word_t predicates = c->pr; + struct ia64_reg_info *r; + uint8_t *dp, *desc_end; + int ret; + + /* build state record */ + memset (sr, 0, sizeof (*sr)); + for (r = sr->curr.reg; r < sr->curr.reg + IA64_NUM_PREGS; ++r) + r->when = IA64_WHEN_NEVER; + sr->pr_val = predicates; + sr->first_region = 1; + + ret = get_proc_info (c, ip, 1); + if (ret < 0) + return ret; + + if (!c->pi.unwind_info) + { + /* No info, return default unwinder (leaf proc, no mem stack, no + saved regs), rp in b0, pfs in ar.pfs. */ + dprintf ("unwind.parser: no unwind info for ip=0x%lx\n", (long) ip); + sr->curr.reg[IA64_REG_RP].where = IA64_WHERE_BR; + sr->curr.reg[IA64_REG_RP].when = -1; + sr->curr.reg[IA64_REG_RP].val = 0; + goto out; + } + + sr->when_target = (3 * ((ip & ~0xfUL) - c->pi.start_ip) / 16 + (ip & 0xfUL)); + + if (c->pi.format == UNW_INFO_FORMAT_TABLE) + { + dp = c->pi.unwind_info; + desc_end = dp + c->pi.unwind_info_size; + while (!sr->done && dp < desc_end) + dp = unw_decode (dp, sr->in_body, sr); + ret = 0; + } + else + ret = parse_dynamic (c, sr); + + put_unwind_info (c, &c->pi); + + if (ret < 0) + return ret; + + if (sr->when_target > sr->epilogue_start) + { + /* sp has been restored and all values on the memory stack below + psp also have been restored. */ + sr->curr.reg[IA64_REG_PSP].val = 0; + sr->curr.reg[IA64_REG_PSP].where = IA64_WHERE_NONE; + sr->curr.reg[IA64_REG_PSP].when = IA64_WHEN_NEVER; + for (r = sr->curr.reg; r < sr->curr.reg + IA64_NUM_PREGS; ++r) + if ((r->where == IA64_WHERE_PSPREL && r->val <= 0x10) + || r->where == IA64_WHERE_SPREL) + { + r->val = 0; + r->where = IA64_WHERE_NONE; + r->when = IA64_WHEN_NEVER; + } + } + + /* If RP did't get saved, generate entry for the return link + register. */ + if (sr->curr.reg[IA64_REG_RP].when >= sr->when_target) + { + sr->curr.reg[IA64_REG_RP].where = IA64_WHERE_BR; + sr->curr.reg[IA64_REG_RP].when = -1; + sr->curr.reg[IA64_REG_RP].val = sr->return_link_reg; + } + + out: +#if UNW_DEBUG + if (unw.debug_level > 0) + { + printf ("unwind: state record for func 0x%lx, t=%u (flags=0x%lx):\n", + (long) c->pi.start_ip, sr->when_target, (long) c->pi.flags); + for (r = sr->curr.reg; r < sr->curr.reg + IA64_NUM_PREGS; ++r) + { + if (r->where != IA64_WHERE_NONE || r->when != IA64_WHEN_NEVER) + { + printf (" %s <- ", unw.preg_name[r - sr->curr.reg]); + switch (r->where) + { + case IA64_WHERE_GR: + printf ("r%lu", (long) r->val); + break; + case IA64_WHERE_FR: + printf ("f%lu", (long) r->val); + break; + case IA64_WHERE_BR: + printf ("b%lu", (long) r->val); + break; + case IA64_WHERE_SPREL: + printf ("[sp+0x%lx]", (long) r->val); + break; + case IA64_WHERE_PSPREL: + printf ("[psp+0x%lx]", (long) r->val); + break; + case IA64_WHERE_NONE: + printf ("%s+0x%lx", + unw.preg_name[r - sr->curr.reg], (long) r->val); + break; + default: + printf ("BADWHERE(%d)", r->where); + break; + } + printf ("\t\t%d\n", r->when); + } + } + } +#endif + return 0; +} + +HIDDEN int +ia64_create_state_record (struct cursor *c, struct ia64_state_record *sr) +{ + return create_state_record_for(c, sr, c->ip); +} + +HIDDEN int +ia64_free_state_record (struct ia64_state_record *sr) +{ + struct ia64_labeled_state *ls, *next; + + /* free labeled register states & stack: */ + + for (ls = sr->labeled_states; ls; ls = next) + { + next = ls->next; + free_state_stack (&ls->saved_state); + free_labeled_state (ls); + } + free_state_stack (&sr->curr); + + return 0; +} + +HIDDEN int +ia64_make_proc_info (struct cursor *c) +{ + if (c->as->caching_policy != UNW_CACHE_NONE) + { + struct ia64_script *script = ia64_script_lookup (c); + + if (script) + { + c->pi = script->pi; + return 0; + } + } + return get_proc_info (c, c->ip, 0); +}