/* libunwind - a platform-independent unwind library Copyright (C) 2001-2003 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, "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) { fprintf (stderr, "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) { 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_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) NELEMS (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], 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) { sr->abi_marker = (abi << 8) | 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->when_sp_restored = 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, "libunwind: 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, "libunwind: 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_IP #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_IP; 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 inline 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, len; int8_t qp; int memory; for (r = proc->regions; r; r = r->next) { len = r->insn_count; if (len < 0) { if (r->next) { debug (10, "libunwind: negative region length allowed in last " "region only!"); return -UNW_EINVAL; } len = -len; /* hack old region info to set the start where we need it: */ sr->region_start = (di->end_ip - di->start_ip) / 0x10 * 3 - len; sr->region_len = 0; } /* all regions are treated as prologue regions: */ desc_prologue (0, len, 0, 0, sr); if (sr->done) return 0; for (op = r->op; op < r->op + r->op_count; ++op) { when = op->when; val = op->val; qp = op->qp; if (!desc_is_active (qp, when, sr)) continue; when = sr->region_start + MIN ((int) when, sr->region_len - 1); 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; val = 0x10 - val; 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; } desc_mem_stack_f (when, -((int64_t) val / 16), sr); } else { dprintf ("unwind.%s: can only ADD to stack-pointer\n", __FUNCTION__); return -UNW_EBADREG; } break; case UNW_DYN_POP_FRAMES: sr->when_sp_restored = 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; } } 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; sr->return_link_reg = 0; 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. */ debug (1, "unwind.parser: no unwind info for ip=0x%lx (gp=%lx)\n", (long) ip, (long) c->pi.gp); sr->curr.reg[IA64_REG_IP].where = IA64_WHERE_BR; sr->curr.reg[IA64_REG_IP].when = -1; sr->curr.reg[IA64_REG_IP].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->when_sp_restored) { /* 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_IP].when >= sr->when_target) { sr->curr.reg[IA64_REG_IP].where = IA64_WHERE_BR; sr->curr.reg[IA64_REG_IP].when = -1; sr->curr.reg[IA64_REG_IP].val = sr->return_link_reg; } if (sr->when_target > sr->curr.reg[IA64_REG_BSP].when && sr->when_target > sr->curr.reg[IA64_REG_BSPSTORE].when && sr->when_target > sr->curr.reg[IA64_REG_RNAT].when) { debug (10, "libunwind: func 0x%lx may switch the register-backing-store\n", c->pi.start_ip); c->pi.flags |= UNW_PI_FLAG_IA64_RBS_SWITCH; } out: #if UNW_DEBUG if (unw.debug_level > 0) { dprintf ("libunwind: 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) { dprintf (" %s <- ", unw.preg_name[r - sr->curr.reg]); switch (r->where) { case IA64_WHERE_GR: dprintf ("r%lu", (long) r->val); break; case IA64_WHERE_FR: dprintf ("f%lu", (long) r->val); break; case IA64_WHERE_BR: dprintf ("b%lu", (long) r->val); break; case IA64_WHERE_SPREL: dprintf ("[sp+0x%lx]", (long) r->val); break; case IA64_WHERE_PSPREL: dprintf ("[psp+0x%lx]", (long) r->val); break; case IA64_WHERE_NONE: dprintf ("%s+0x%lx", unw.preg_name[r - sr->curr.reg], (long) r->val); break; default: dprintf ("BADWHERE(%d)", r->where); break; } dprintf ("\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 || ia64_get_cached_proc_info (c) < 0) /* Lookup it up the slow way... */ return get_proc_info (c, c->ip, 0); return 0; }