| /* Subroutines needed for unwinding IA-64 standard format stack frame |
| info for exception handling. |
| Copyright (C) 1997-2021 Free Software Foundation, Inc. |
| Contributed by Andrew MacLeod <amacleod@cygnus.com> |
| Andrew Haley <aph@cygnus.com> |
| David Mosberger-Tang <davidm@hpl.hp.com> |
| |
| This file is part of GCC. |
| |
| GCC is free software; you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation; either version 3, or (at your option) |
| any later version. |
| |
| GCC is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| Under Section 7 of GPL version 3, you are granted additional |
| permissions described in the GCC Runtime Library Exception, version |
| 3.1, as published by the Free Software Foundation. |
| |
| You should have received a copy of the GNU General Public License and |
| a copy of the GCC Runtime Library Exception along with this program; |
| see the files COPYING3 and COPYING.RUNTIME respectively. If not, see |
| <http://www.gnu.org/licenses/>. */ |
| |
| #include "tconfig.h" |
| #include "tsystem.h" |
| #include "coretypes.h" |
| #include "tm.h" |
| #include "libgcc_tm.h" |
| #include "unwind.h" |
| #include "unwind-ia64.h" |
| #include "unwind-compat.h" |
| #include "ia64intrin.h" |
| |
| /* This isn't thread safe, but nice for occasional tests. */ |
| #undef ENABLE_MALLOC_CHECKING |
| |
| #ifndef __USING_SJLJ_EXCEPTIONS__ |
| |
| enum unw_application_register |
| { |
| UNW_AR_BSP, |
| UNW_AR_BSPSTORE, |
| UNW_AR_PFS, |
| UNW_AR_RNAT, |
| UNW_AR_UNAT, |
| UNW_AR_LC, |
| UNW_AR_EC, |
| UNW_AR_FPSR, |
| UNW_AR_RSC, |
| UNW_AR_CCV |
| }; |
| |
| enum unw_register_index |
| { |
| /* Primary UNAT. */ |
| UNW_REG_PRI_UNAT_GR, |
| UNW_REG_PRI_UNAT_MEM, |
| |
| /* Memory Stack. */ |
| UNW_REG_PSP, /* previous memory stack pointer */ |
| |
| /* Register Stack. */ |
| UNW_REG_BSP, /* register stack pointer */ |
| UNW_REG_BSPSTORE, |
| UNW_REG_PFS, /* previous function state */ |
| UNW_REG_RNAT, |
| /* Return Pointer. */ |
| UNW_REG_RP, |
| |
| /* Special preserved registers. */ |
| UNW_REG_UNAT, UNW_REG_PR, UNW_REG_LC, UNW_REG_FPSR, |
| |
| /* Non-stacked general registers. */ |
| UNW_REG_R2, |
| UNW_REG_R4 = UNW_REG_R2 + 2, |
| UNW_REG_R7 = UNW_REG_R2 + 5, |
| UNW_REG_R31 = UNW_REG_R2 + 29, |
| |
| /* Non-stacked floating point registers. */ |
| UNW_REG_F2, |
| UNW_REG_F5 = UNW_REG_F2 + 3, |
| UNW_REG_F16 = UNW_REG_F2 + 14, |
| UNW_REG_F31 = UNW_REG_F2 + 29, |
| |
| /* Branch registers. */ |
| UNW_REG_B0, UNW_REG_B1, |
| UNW_REG_B5 = UNW_REG_B1 + 4, |
| |
| UNW_NUM_REGS |
| }; |
| |
| enum unw_where |
| { |
| UNW_WHERE_NONE, /* register isn't saved at all */ |
| UNW_WHERE_GR, /* register is saved in a general register */ |
| UNW_WHERE_FR, /* register is saved in a floating-point register */ |
| UNW_WHERE_BR, /* register is saved in a branch register */ |
| UNW_WHERE_SPREL, /* register is saved on memstack (sp-relative) */ |
| UNW_WHERE_PSPREL, /* register is saved on memstack (psp-relative) */ |
| |
| /* At the end of each prologue these locations get resolved to |
| UNW_WHERE_PSPREL and UNW_WHERE_GR, respectively. */ |
| UNW_WHERE_SPILL_HOME, /* register is saved in its spill home */ |
| UNW_WHERE_GR_SAVE /* register is saved in next general register */ |
| }; |
| |
| #define UNW_WHEN_NEVER 0x7fffffff |
| |
| struct unw_reg_info |
| { |
| unw_word val; /* save location: register number or offset */ |
| enum unw_where where; /* where the register gets saved */ |
| int when; /* when the register gets saved */ |
| }; |
| |
| struct unw_reg_state { |
| struct unw_reg_state *next; /* next (outer) element on state stack */ |
| struct unw_reg_info reg[UNW_NUM_REGS]; /* register save locations */ |
| }; |
| |
| struct unw_labeled_state { |
| struct unw_labeled_state *next; /* next labeled state (or NULL) */ |
| unw_word label; /* label for this state */ |
| struct unw_reg_state saved_state; |
| }; |
| |
| typedef struct unw_state_record |
| { |
| unsigned int first_region : 1; /* is this the first region? */ |
| unsigned int done : 1; /* are we done scanning descriptors? */ |
| unsigned int any_spills : 1; /* got any register spills? */ |
| unsigned int in_body : 1; /* are we inside a body? */ |
| unsigned int no_reg_stack_frame : 1; /* Don't adjust bsp for i&l regs */ |
| unsigned char *imask; /* imask of spill_mask record or NULL */ |
| unw_word pr_val; /* predicate values */ |
| unw_word pr_mask; /* predicate mask */ |
| unw_sword spill_offset; /* psp-relative offset for spill base */ |
| int region_start; |
| int region_len; |
| int epilogue_start; |
| int epilogue_count; |
| int when_target; |
| |
| unsigned char gr_save_loc; /* next general register to use for saving */ |
| unsigned char return_link_reg; /* branch register for return link */ |
| unsigned short unwabi; |
| |
| struct unw_labeled_state *labeled_states; /* list of all labeled states */ |
| struct unw_reg_state curr; /* current state */ |
| |
| _Unwind_Personality_Fn personality; |
| |
| } _Unwind_FrameState; |
| |
| enum unw_nat_type |
| { |
| UNW_NAT_NONE, /* NaT not represented */ |
| UNW_NAT_VAL, /* NaT represented by NaT value (fp reg) */ |
| UNW_NAT_MEMSTK, /* NaT value is in unat word at offset OFF */ |
| UNW_NAT_REGSTK /* NaT is in rnat */ |
| }; |
| |
| struct unw_stack |
| { |
| unw_word limit; |
| unw_word top; |
| }; |
| |
| struct _Unwind_Context |
| { |
| /* Initial frame info. */ |
| unw_word rnat; /* rse nat collection */ |
| unw_word regstk_top; /* lowest address of rbs stored register |
| which uses context->rnat collection */ |
| |
| /* Current frame info. */ |
| unw_word bsp; /* backing store pointer value |
| corresponding to psp. */ |
| unw_word sp; /* stack pointer value */ |
| unw_word psp; /* previous sp value */ |
| unw_word rp; /* return pointer */ |
| unw_word pr; /* predicate collection */ |
| |
| unw_word region_start;/* start of unwind region */ |
| unw_word gp; /* global pointer value */ |
| void *lsda; /* language specific data area */ |
| |
| /* Preserved state. */ |
| unw_word *bsp_loc; /* previous bsp save location |
| Appears to be write-only? */ |
| unw_word *bspstore_loc; |
| unw_word *pfs_loc; /* Save location for pfs in current |
| (corr. to sp) frame. Target |
| contains cfm for caller. */ |
| unw_word *signal_pfs_loc;/* Save location for pfs in current |
| signal frame. Target contains |
| pfs for caller. */ |
| unw_word *pri_unat_loc; |
| unw_word *unat_loc; |
| unw_word *lc_loc; |
| unw_word *fpsr_loc; |
| |
| unw_word eh_data[4]; |
| |
| struct unw_ireg |
| { |
| unw_word *loc; |
| struct unw_ireg_nat |
| { |
| enum unw_nat_type type : 3; |
| unw_sword off : 61; /* NaT word is at loc+nat.off */ |
| } nat; |
| } ireg[32 - 2]; /* Indexed by <register number> - 2 */ |
| |
| unw_word *br_loc[8]; |
| void *fr_loc[32 - 2]; |
| |
| /* ??? We initially point pri_unat_loc here. The entire NAT bit |
| logic needs work. */ |
| unw_word initial_unat; |
| }; |
| |
| /* Implicit register save order. See section 11.4.2.3 Rules for Using |
| Unwind Descriptors, rule 3. */ |
| |
| static unsigned char const save_order[] = |
| { |
| UNW_REG_RP, UNW_REG_PFS, UNW_REG_PSP, UNW_REG_PR, |
| UNW_REG_UNAT, UNW_REG_LC, UNW_REG_FPSR, UNW_REG_PRI_UNAT_GR |
| }; |
| |
| |
| #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) |
| |
| /* MASK is a bitmap describing the allocation state of emergency buffers, |
| with bit set indicating free. Return >= 0 if allocation is successful; |
| < 0 if failure. */ |
| |
| static inline int |
| atomic_alloc (unsigned int *mask) |
| { |
| unsigned int old = *mask, ret, new; |
| |
| while (1) |
| { |
| if (old == 0) |
| return -1; |
| ret = old & -old; |
| new = old & ~ret; |
| new = __sync_val_compare_and_swap (mask, old, new); |
| if (old == new) |
| break; |
| old = new; |
| } |
| |
| return __builtin_ffs (ret) - 1; |
| } |
| |
| /* Similarly, free an emergency buffer. */ |
| |
| static inline void |
| atomic_free (unsigned int *mask, int bit) |
| { |
| __sync_xor_and_fetch (mask, 1 << bit); |
| } |
| |
| |
| #define SIZE(X) (sizeof(X) / sizeof(*(X))) |
| #define MASK_FOR(X) ((2U << (SIZE (X) - 1)) - 1) |
| #define PTR_IN(X, P) ((P) >= (X) && (P) < (X) + SIZE (X)) |
| |
| static struct unw_reg_state emergency_reg_state[32]; |
| static unsigned int emergency_reg_state_free = MASK_FOR (emergency_reg_state); |
| |
| static struct unw_labeled_state emergency_labeled_state[8]; |
| static unsigned int emergency_labeled_state_free = MASK_FOR (emergency_labeled_state); |
| |
| #ifdef ENABLE_MALLOC_CHECKING |
| static int reg_state_alloced; |
| static int labeled_state_alloced; |
| #endif |
| |
| /* Allocation and deallocation of structures. */ |
| |
| static struct unw_reg_state * |
| alloc_reg_state (void) |
| { |
| struct unw_reg_state *rs; |
| |
| #ifdef ENABLE_MALLOC_CHECKING |
| reg_state_alloced++; |
| #endif |
| |
| rs = malloc (sizeof (struct unw_reg_state)); |
| if (!rs) |
| { |
| int n = atomic_alloc (&emergency_reg_state_free); |
| if (n >= 0) |
| rs = &emergency_reg_state[n]; |
| } |
| |
| return rs; |
| } |
| |
| static void |
| free_reg_state (struct unw_reg_state *rs) |
| { |
| #ifdef ENABLE_MALLOC_CHECKING |
| reg_state_alloced--; |
| #endif |
| |
| if (PTR_IN (emergency_reg_state, rs)) |
| atomic_free (&emergency_reg_state_free, rs - emergency_reg_state); |
| else |
| free (rs); |
| } |
| |
| static struct unw_labeled_state * |
| alloc_label_state (void) |
| { |
| struct unw_labeled_state *ls; |
| |
| #ifdef ENABLE_MALLOC_CHECKING |
| labeled_state_alloced++; |
| #endif |
| |
| ls = malloc(sizeof(struct unw_labeled_state)); |
| if (!ls) |
| { |
| int n = atomic_alloc (&emergency_labeled_state_free); |
| if (n >= 0) |
| ls = &emergency_labeled_state[n]; |
| } |
| |
| return ls; |
| } |
| |
| static void |
| free_label_state (struct unw_labeled_state *ls) |
| { |
| #ifdef ENABLE_MALLOC_CHECKING |
| labeled_state_alloced--; |
| #endif |
| |
| if (PTR_IN (emergency_labeled_state, ls)) |
| atomic_free (&emergency_labeled_state_free, emergency_labeled_state - ls); |
| else |
| free (ls); |
| } |
| |
| /* Routines to manipulate the state stack. */ |
| |
| static void |
| push (struct unw_state_record *sr) |
| { |
| struct unw_reg_state *rs = alloc_reg_state (); |
| memcpy (rs, &sr->curr, sizeof (*rs)); |
| sr->curr.next = rs; |
| } |
| |
| static void |
| pop (struct unw_state_record *sr) |
| { |
| struct unw_reg_state *rs = sr->curr.next; |
| |
| if (!rs) |
| abort (); |
| 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 unw_reg_state * |
| dup_state_stack (struct unw_reg_state *rs) |
| { |
| struct unw_reg_state *copy, *prev = NULL, *first = NULL; |
| |
| while (rs) |
| { |
| copy = alloc_reg_state (); |
| 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 unw_reg_state *rs) |
| { |
| struct unw_reg_state *p, *next; |
| |
| for (p = rs->next; p != NULL; p = next) |
| { |
| next = p->next; |
| free_reg_state (p); |
| } |
| rs->next = NULL; |
| } |
| |
| /* Free all labeled states. */ |
| |
| static void |
| free_label_states (struct unw_labeled_state *ls) |
| { |
| struct unw_labeled_state *next; |
| |
| for (; ls ; ls = next) |
| { |
| next = ls->next; |
| |
| free_state_stack (&ls->saved_state); |
| free_label_state (ls); |
| } |
| } |
| |
| /* Unwind decoder routines */ |
| |
| static enum unw_register_index __attribute__((const)) |
| decode_abreg (unsigned char abreg, int memory) |
| { |
| switch (abreg) |
| { |
| #if TARGET_ABI_OPEN_VMS |
| /* OpenVMS Calling Standard specifies R3 - R31. */ |
| case 0x03 ... 0x1f: return UNW_REG_R2 + (abreg - 0x02); |
| #else |
| /* Standard Intel ABI specifies GR 4 - 7. */ |
| case 0x04 ... 0x07: return UNW_REG_R4 + (abreg - 0x04); |
| #endif |
| case 0x22 ... 0x25: return UNW_REG_F2 + (abreg - 0x22); |
| case 0x30 ... 0x3f: return UNW_REG_F16 + (abreg - 0x30); |
| case 0x41 ... 0x45: return UNW_REG_B1 + (abreg - 0x41); |
| case 0x60: return UNW_REG_PR; |
| case 0x61: return UNW_REG_PSP; |
| case 0x62: return memory ? UNW_REG_PRI_UNAT_MEM : UNW_REG_PRI_UNAT_GR; |
| case 0x63: return UNW_REG_RP; |
| case 0x64: return UNW_REG_BSP; |
| case 0x65: return UNW_REG_BSPSTORE; |
| case 0x66: return UNW_REG_RNAT; |
| case 0x67: return UNW_REG_UNAT; |
| case 0x68: return UNW_REG_FPSR; |
| case 0x69: return UNW_REG_PFS; |
| case 0x6a: return UNW_REG_LC; |
| default: |
| abort (); |
| } |
| } |
| |
| static void |
| set_reg (struct unw_reg_info *reg, enum unw_where where, |
| int when, unw_word val) |
| { |
| reg->val = val; |
| reg->where = where; |
| if (reg->when == UNW_WHEN_NEVER) |
| reg->when = when; |
| } |
| |
| static void |
| alloc_spill_area (unw_word *offp, unw_word regsize, |
| struct unw_reg_info *lo, struct unw_reg_info *hi) |
| { |
| struct unw_reg_info *reg; |
| |
| for (reg = hi; reg >= lo; --reg) |
| { |
| if (reg->where == UNW_WHERE_SPILL_HOME) |
| { |
| reg->where = UNW_WHERE_PSPREL; |
| *offp -= regsize; |
| reg->val = *offp; |
| } |
| } |
| } |
| |
| static inline void |
| spill_next_when (struct unw_reg_info **regp, struct unw_reg_info *lim, |
| unw_word t) |
| { |
| struct unw_reg_info *reg; |
| |
| for (reg = *regp; reg <= lim; ++reg) |
| { |
| if (reg->where == UNW_WHERE_SPILL_HOME) |
| { |
| reg->when = t; |
| *regp = reg + 1; |
| return; |
| } |
| } |
| /* Excess spill. */ |
| abort (); |
| } |
| |
| static void |
| finish_prologue (struct unw_state_record *sr) |
| { |
| struct unw_reg_info *reg; |
| unw_word 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 (save_order); ++i) |
| { |
| reg = sr->curr.reg + save_order[i]; |
| if (reg->where == UNW_WHERE_GR_SAVE) |
| { |
| reg->where = UNW_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) |
| { |
| static unsigned char const limit[3] = { |
| UNW_REG_F31, UNW_REG_R7, UNW_REG_B5 |
| }; |
| |
| unsigned char kind, mask = 0, *cp = sr->imask; |
| int t; |
| struct unw_reg_info *(regs[3]); |
| |
| regs[0] = sr->curr.reg + UNW_REG_F2; |
| regs[1] = sr->curr.reg + UNW_REG_R4; |
| regs[2] = sr->curr.reg + UNW_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 + UNW_REG_F2, |
| sr->curr.reg + UNW_REG_F31); |
| alloc_spill_area (&off, 8, sr->curr.reg + UNW_REG_B1, |
| sr->curr.reg + UNW_REG_B5); |
| alloc_spill_area (&off, 8, sr->curr.reg + UNW_REG_R4, |
| sr->curr.reg + UNW_REG_R7); |
| } |
| } |
| |
| /* |
| * Region header descriptors. |
| */ |
| |
| static void |
| desc_prologue (int body, unw_word rlen, unsigned char mask, |
| unsigned char grsave, struct unw_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 = UNW_WHEN_NEVER; |
| |
| if (!body) |
| push (sr); |
| |
| sr->region_start += sr->region_len; |
| sr->region_len = rlen; |
| sr->in_body = body; |
| |
| if (!body) |
| { |
| for (i = 0; i < 4; ++i) |
| { |
| if (mask & 0x8) |
| set_reg (sr->curr.reg + save_order[i], UNW_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 unw_state_record *sr) |
| { |
| sr->unwabi = (abi << 8) | context; |
| } |
| |
| static inline void |
| desc_br_gr (unsigned char brmask, unsigned char gr, |
| struct unw_state_record *sr) |
| { |
| int i; |
| |
| for (i = 0; i < 5; ++i) |
| { |
| if (brmask & 1) |
| set_reg (sr->curr.reg + UNW_REG_B1 + i, UNW_WHERE_GR, |
| sr->region_start + sr->region_len - 1, gr++); |
| brmask >>= 1; |
| } |
| } |
| |
| static inline void |
| desc_br_mem (unsigned char brmask, struct unw_state_record *sr) |
| { |
| int i; |
| |
| for (i = 0; i < 5; ++i) |
| { |
| if (brmask & 1) |
| { |
| set_reg (sr->curr.reg + UNW_REG_B1 + i, UNW_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 unw_state_record *sr) |
| { |
| int i; |
| |
| for (i = 0; i < 4; ++i) |
| { |
| if ((grmask & 1) != 0) |
| { |
| set_reg (sr->curr.reg + UNW_REG_R4 + i, UNW_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) |
| { |
| enum unw_register_index base = i < 4 ? UNW_REG_F2 : UNW_REG_F16 - 4; |
| set_reg (sr->curr.reg + base + i, UNW_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 unw_state_record *sr) |
| { |
| int i; |
| |
| for (i = 0; i < 4; ++i) |
| { |
| if ((frmask & 1) != 0) |
| { |
| set_reg (sr->curr.reg + UNW_REG_F2 + i, UNW_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 unw_state_record *sr) |
| { |
| int i; |
| |
| for (i = 0; i < 4; ++i) |
| { |
| if ((grmask & 1) != 0) |
| set_reg (sr->curr.reg + UNW_REG_R4 + i, UNW_WHERE_GR, |
| sr->region_start + sr->region_len - 1, gr++); |
| grmask >>= 1; |
| } |
| } |
| |
| static inline void |
| desc_gr_mem (unsigned char grmask, struct unw_state_record *sr) |
| { |
| int i; |
| |
| for (i = 0; i < 4; ++i) |
| { |
| if ((grmask & 1) != 0) |
| { |
| set_reg (sr->curr.reg + UNW_REG_R4 + i, UNW_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 unw_state_record *sr) |
| { |
| set_reg (sr->curr.reg + UNW_REG_PSP, UNW_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 unw_state_record *sr) |
| { |
| sr->curr.reg[UNW_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 unw_state_record *sr) |
| { |
| set_reg (sr->curr.reg + reg, UNW_WHERE_GR, |
| sr->region_start + sr->region_len - 1, dst); |
| } |
| |
| static inline void |
| desc_reg_psprel (unsigned char reg, unw_word pspoff, |
| struct unw_state_record *sr) |
| { |
| set_reg (sr->curr.reg + reg, UNW_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 unw_state_record *sr) |
| { |
| set_reg (sr->curr.reg + reg, UNW_WHERE_SPREL, |
| sr->region_start + sr->region_len - 1, |
| 4*spoff); |
| } |
| |
| static inline void |
| desc_rp_br (unsigned char dst, struct unw_state_record *sr) |
| { |
| sr->return_link_reg = dst; |
| } |
| |
| static inline void |
| desc_reg_when (unsigned char regnum, unw_word t, struct unw_state_record *sr) |
| { |
| struct unw_reg_info *reg = sr->curr.reg + regnum; |
| |
| if (reg->where == UNW_WHERE_NONE) |
| reg->where = UNW_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 unw_state_record *sr) |
| { |
| sr->spill_offset = 0x10 - 4*pspoff; |
| } |
| |
| static inline unsigned char * |
| desc_spill_mask (unsigned char *imaskp, struct unw_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 unw_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 unw_state_record *sr) |
| { |
| struct unw_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; |
| } |
| } |
| abort (); |
| } |
| |
| static inline void |
| desc_label_state (unw_word label, struct unw_state_record *sr) |
| { |
| struct unw_labeled_state *ls = alloc_label_state (); |
| |
| 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 unw_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 unw_state_record *sr) |
| { |
| struct unw_reg_info *r; |
| |
| if (! desc_is_active (qp, t, sr)) |
| return; |
| |
| r = sr->curr.reg + decode_abreg (abreg, 0); |
| r->where = UNW_WHERE_NONE; |
| r->when = sr->region_start + MIN ((int)t, sr->region_len - 1); |
| 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 unw_state_record *sr) |
| { |
| enum unw_where where = UNW_WHERE_GR; |
| struct unw_reg_info *r; |
| |
| if (! desc_is_active (qp, t, sr)) |
| return; |
| |
| if (x) |
| where = UNW_WHERE_BR; |
| else if (ytreg & 0x80) |
| where = UNW_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 unw_state_record *sr) |
| { |
| struct unw_reg_info *r; |
| |
| if (! desc_is_active (qp, t, sr)) |
| return; |
| |
| r = sr->curr.reg + decode_abreg (abreg, 1); |
| r->where = UNW_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 unw_state_record *sr) |
| { |
| struct unw_reg_info *r; |
| |
| if (! desc_is_active (qp, t, sr)) |
| return; |
| |
| r = sr->curr.reg + decode_abreg (abreg, 1); |
| r->where = UNW_WHERE_SPREL; |
| r->when = sr->region_start + MIN ((int)t, sr->region_len - 1); |
| r->val = 4*spoff; |
| } |
| |
| |
| #define UNW_DEC_BAD_CODE(code) abort (); |
| |
| /* 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(UNW_REG_PRI_UNAT_GR,t,arg) |
| #define UNW_DEC_PRIUNAT_WHEN_MEM(fmt,t,arg) desc_reg_when(UNW_REG_PRI_UNAT_MEM,t,arg) |
| #define UNW_DEC_PRIUNAT_GR(fmt,r,arg) desc_reg_gr(UNW_REG_PRI_UNAT_GR,r,arg) |
| #define UNW_DEC_PRIUNAT_PSPREL(fmt,o,arg) desc_reg_psprel(UNW_REG_PRI_UNAT_MEM,o,arg) |
| #define UNW_DEC_PRIUNAT_SPREL(fmt,o,arg) desc_reg_sprel(UNW_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) |
| |
| |
| /* |
| * Generic IA-64 unwind info decoder. |
| * |
| * This file is used both by the Linux kernel and objdump. Please keep |
| * the copies of this file in sync. |
| * |
| * You need to customize the decoder by defining the following |
| * macros/constants before including this file: |
| * |
| * Types: |
| * unw_word Unsigned integer type with at least 64 bits |
| * |
| * Register names: |
| * UNW_REG_BSP |
| * UNW_REG_BSPSTORE |
| * UNW_REG_FPSR |
| * UNW_REG_LC |
| * UNW_REG_PFS |
| * UNW_REG_PR |
| * UNW_REG_RNAT |
| * UNW_REG_PSP |
| * UNW_REG_RP |
| * UNW_REG_UNAT |
| * |
| * Decoder action macros: |
| * UNW_DEC_BAD_CODE(code) |
| * UNW_DEC_ABI(fmt,abi,context,arg) |
| * UNW_DEC_BR_GR(fmt,brmask,gr,arg) |
| * UNW_DEC_BR_MEM(fmt,brmask,arg) |
| * UNW_DEC_COPY_STATE(fmt,label,arg) |
| * UNW_DEC_EPILOGUE(fmt,t,ecount,arg) |
| * UNW_DEC_FRGR_MEM(fmt,grmask,frmask,arg) |
| * UNW_DEC_FR_MEM(fmt,frmask,arg) |
| * UNW_DEC_GR_GR(fmt,grmask,gr,arg) |
| * UNW_DEC_GR_MEM(fmt,grmask,arg) |
| * UNW_DEC_LABEL_STATE(fmt,label,arg) |
| * UNW_DEC_MEM_STACK_F(fmt,t,size,arg) |
| * UNW_DEC_MEM_STACK_V(fmt,t,arg) |
| * UNW_DEC_PRIUNAT_GR(fmt,r,arg) |
| * UNW_DEC_PRIUNAT_WHEN_GR(fmt,t,arg) |
| * UNW_DEC_PRIUNAT_WHEN_MEM(fmt,t,arg) |
| * UNW_DEC_PRIUNAT_WHEN_PSPREL(fmt,pspoff,arg) |
| * UNW_DEC_PRIUNAT_WHEN_SPREL(fmt,spoff,arg) |
| * UNW_DEC_PROLOGUE(fmt,body,rlen,arg) |
| * UNW_DEC_PROLOGUE_GR(fmt,rlen,mask,grsave,arg) |
| * UNW_DEC_REG_PSPREL(fmt,reg,pspoff,arg) |
| * UNW_DEC_REG_REG(fmt,src,dst,arg) |
| * UNW_DEC_REG_SPREL(fmt,reg,spoff,arg) |
| * UNW_DEC_REG_WHEN(fmt,reg,t,arg) |
| * UNW_DEC_RESTORE(fmt,t,abreg,arg) |
| * UNW_DEC_RESTORE_P(fmt,qp,t,abreg,arg) |
| * UNW_DEC_SPILL_BASE(fmt,pspoff,arg) |
| * UNW_DEC_SPILL_MASK(fmt,imaskp,arg) |
| * UNW_DEC_SPILL_PSPREL(fmt,t,abreg,pspoff,arg) |
| * UNW_DEC_SPILL_PSPREL_P(fmt,qp,t,abreg,pspoff,arg) |
| * UNW_DEC_SPILL_REG(fmt,t,abreg,x,ytreg,arg) |
| * UNW_DEC_SPILL_REG_P(fmt,qp,t,abreg,x,ytreg,arg) |
| * UNW_DEC_SPILL_SPREL(fmt,t,abreg,spoff,arg) |
| * UNW_DEC_SPILL_SPREL_P(fmt,qp,t,abreg,pspoff,arg) |
| */ |
| |
| static unw_word |
| unw_decode_uleb128 (unsigned char **dpp) |
| { |
| unsigned shift = 0; |
| unw_word byte, result = 0; |
| unsigned char *bp = *dpp; |
| |
| while (1) |
| { |
| byte = *bp++; |
| result |= (byte & 0x7f) << shift; |
| if ((byte & 0x80) == 0) |
| break; |
| shift += 7; |
| } |
| *dpp = bp; |
| return result; |
| } |
| |
| static unsigned char * |
| unw_decode_x1 (unsigned char *dp, |
| unsigned char code __attribute__((unused)), |
| void *arg) |
| { |
| unsigned char byte1, abreg; |
| unw_word t, off; |
| |
| byte1 = *dp++; |
| t = unw_decode_uleb128 (&dp); |
| off = unw_decode_uleb128 (&dp); |
| abreg = (byte1 & 0x7f); |
| if (byte1 & 0x80) |
| UNW_DEC_SPILL_SPREL(X1, t, abreg, off, arg); |
| else |
| UNW_DEC_SPILL_PSPREL(X1, t, abreg, off, arg); |
| return dp; |
| } |
| |
| static unsigned char * |
| unw_decode_x2 (unsigned char *dp, |
| unsigned char code __attribute__((unused)), |
| void *arg) |
| { |
| unsigned char byte1, byte2, abreg, x, ytreg; |
| unw_word t; |
| |
| byte1 = *dp++; byte2 = *dp++; |
| t = unw_decode_uleb128 (&dp); |
| abreg = (byte1 & 0x7f); |
| ytreg = byte2; |
| x = (byte1 >> 7) & 1; |
| if ((byte1 & 0x80) == 0 && ytreg == 0) |
| UNW_DEC_RESTORE(X2, t, abreg, arg); |
| else |
| UNW_DEC_SPILL_REG(X2, t, abreg, x, ytreg, arg); |
| return dp; |
| } |
| |
| static unsigned char * |
| unw_decode_x3 (unsigned char *dp, |
| unsigned char code __attribute__((unused)), |
| void *arg) |
| { |
| unsigned char byte1, byte2, abreg, qp; |
| unw_word t, off; |
| |
| byte1 = *dp++; byte2 = *dp++; |
| t = unw_decode_uleb128 (&dp); |
| off = unw_decode_uleb128 (&dp); |
| |
| qp = (byte1 & 0x3f); |
| abreg = (byte2 & 0x7f); |
| |
| if (byte1 & 0x80) |
| UNW_DEC_SPILL_SPREL_P(X3, qp, t, abreg, off, arg); |
| else |
| UNW_DEC_SPILL_PSPREL_P(X3, qp, t, abreg, off, arg); |
| return dp; |
| } |
| |
| static unsigned char * |
| unw_decode_x4 (unsigned char *dp, |
| unsigned char code __attribute__((unused)), |
| void *arg) |
| { |
| unsigned char byte1, byte2, byte3, qp, abreg, x, ytreg; |
| unw_word t; |
| |
| byte1 = *dp++; byte2 = *dp++; byte3 = *dp++; |
| t = unw_decode_uleb128 (&dp); |
| |
| qp = (byte1 & 0x3f); |
| abreg = (byte2 & 0x7f); |
| x = (byte2 >> 7) & 1; |
| ytreg = byte3; |
| |
| if ((byte2 & 0x80) == 0 && byte3 == 0) |
| UNW_DEC_RESTORE_P(X4, qp, t, abreg, arg); |
| else |
| UNW_DEC_SPILL_REG_P(X4, qp, t, abreg, x, ytreg, arg); |
| return dp; |
| } |
| |
| static unsigned char * |
| unw_decode_r1 (unsigned char *dp, unsigned char code, void *arg) |
| { |
| int body = (code & 0x20) != 0; |
| unw_word rlen; |
| |
| rlen = (code & 0x1f); |
| UNW_DEC_PROLOGUE(R1, body, rlen, arg); |
| return dp; |
| } |
| |
| static unsigned char * |
| unw_decode_r2 (unsigned char *dp, unsigned char code, void *arg) |
| { |
| unsigned char byte1, mask, grsave; |
| unw_word rlen; |
| |
| byte1 = *dp++; |
| |
| mask = ((code & 0x7) << 1) | ((byte1 >> 7) & 1); |
| grsave = (byte1 & 0x7f); |
| rlen = unw_decode_uleb128 (&dp); |
| UNW_DEC_PROLOGUE_GR(R2, rlen, mask, grsave, arg); |
| return dp; |
| } |
| |
| static unsigned char * |
| unw_decode_r3 (unsigned char *dp, unsigned char code, void *arg) |
| { |
| unw_word rlen; |
| |
| rlen = unw_decode_uleb128 (&dp); |
| UNW_DEC_PROLOGUE(R3, ((code & 0x3) == 1), rlen, arg); |
| return dp; |
| } |
| |
| static unsigned char * |
| unw_decode_p1 (unsigned char *dp, unsigned char code, void *arg) |
| { |
| unsigned char brmask = (code & 0x1f); |
| |
| UNW_DEC_BR_MEM(P1, brmask, arg); |
| return dp; |
| } |
| |
| static unsigned char * |
| unw_decode_p2_p5 (unsigned char *dp, unsigned char code, void *arg) |
| { |
| if ((code & 0x10) == 0) |
| { |
| unsigned char byte1 = *dp++; |
| |
| UNW_DEC_BR_GR(P2, ((code & 0xf) << 1) | ((byte1 >> 7) & 1), |
| (byte1 & 0x7f), arg); |
| } |
| else if ((code & 0x08) == 0) |
| { |
| unsigned char byte1 = *dp++, r, dst; |
| |
| r = ((code & 0x7) << 1) | ((byte1 >> 7) & 1); |
| dst = (byte1 & 0x7f); |
| switch (r) |
| { |
| case 0: UNW_DEC_REG_GR(P3, UNW_REG_PSP, dst, arg); break; |
| case 1: UNW_DEC_REG_GR(P3, UNW_REG_RP, dst, arg); break; |
| case 2: UNW_DEC_REG_GR(P3, UNW_REG_PFS, dst, arg); break; |
| case 3: UNW_DEC_REG_GR(P3, UNW_REG_PR, dst, arg); break; |
| case 4: UNW_DEC_REG_GR(P3, UNW_REG_UNAT, dst, arg); break; |
| case 5: UNW_DEC_REG_GR(P3, UNW_REG_LC, dst, arg); break; |
| case 6: UNW_DEC_RP_BR(P3, dst, arg); break; |
| case 7: UNW_DEC_REG_GR(P3, UNW_REG_RNAT, dst, arg); break; |
| case 8: UNW_DEC_REG_GR(P3, UNW_REG_BSP, dst, arg); break; |
| case 9: UNW_DEC_REG_GR(P3, UNW_REG_BSPSTORE, dst, arg); break; |
| case 10: UNW_DEC_REG_GR(P3, UNW_REG_FPSR, dst, arg); break; |
| case 11: UNW_DEC_PRIUNAT_GR(P3, dst, arg); break; |
| default: UNW_DEC_BAD_CODE(r); break; |
| } |
| } |
| else if ((code & 0x7) == 0) |
| UNW_DEC_SPILL_MASK(P4, dp, arg); |
| else if ((code & 0x7) == 1) |
| { |
| unw_word grmask, frmask, byte1, byte2, byte3; |
| |
| byte1 = *dp++; byte2 = *dp++; byte3 = *dp++; |
| grmask = ((byte1 >> 4) & 0xf); |
| frmask = ((byte1 & 0xf) << 16) | (byte2 << 8) | byte3; |
| UNW_DEC_FRGR_MEM(P5, grmask, frmask, arg); |
| } |
| else |
| UNW_DEC_BAD_CODE(code); |
| return dp; |
| } |
| |
| static unsigned char * |
| unw_decode_p6 (unsigned char *dp, unsigned char code, void *arg) |
| { |
| int gregs = (code & 0x10) != 0; |
| unsigned char mask = (code & 0x0f); |
| |
| if (gregs) |
| UNW_DEC_GR_MEM(P6, mask, arg); |
| else |
| UNW_DEC_FR_MEM(P6, mask, arg); |
| return dp; |
| } |
| |
| static unsigned char * |
| unw_decode_p7_p10 (unsigned char *dp, unsigned char code, void *arg) |
| { |
| unsigned char r, byte1, byte2; |
| unw_word t, size; |
| |
| if ((code & 0x10) == 0) |
| { |
| r = (code & 0xf); |
| t = unw_decode_uleb128 (&dp); |
| switch (r) |
| { |
| case 0: |
| size = unw_decode_uleb128 (&dp); |
| UNW_DEC_MEM_STACK_F(P7, t, size, arg); |
| break; |
| |
| case 1: UNW_DEC_MEM_STACK_V(P7, t, arg); break; |
| case 2: UNW_DEC_SPILL_BASE(P7, t, arg); break; |
| case 3: UNW_DEC_REG_SPREL(P7, UNW_REG_PSP, t, arg); break; |
| case 4: UNW_DEC_REG_WHEN(P7, UNW_REG_RP, t, arg); break; |
| case 5: UNW_DEC_REG_PSPREL(P7, UNW_REG_RP, t, arg); break; |
| case 6: UNW_DEC_REG_WHEN(P7, UNW_REG_PFS, t, arg); break; |
| case 7: UNW_DEC_REG_PSPREL(P7, UNW_REG_PFS, t, arg); break; |
| case 8: UNW_DEC_REG_WHEN(P7, UNW_REG_PR, t, arg); break; |
| case 9: UNW_DEC_REG_PSPREL(P7, UNW_REG_PR, t, arg); break; |
| case 10: UNW_DEC_REG_WHEN(P7, UNW_REG_LC, t, arg); break; |
| case 11: UNW_DEC_REG_PSPREL(P7, UNW_REG_LC, t, arg); break; |
| case 12: UNW_DEC_REG_WHEN(P7, UNW_REG_UNAT, t, arg); break; |
| case 13: UNW_DEC_REG_PSPREL(P7, UNW_REG_UNAT, t, arg); break; |
| case 14: UNW_DEC_REG_WHEN(P7, UNW_REG_FPSR, t, arg); break; |
| case 15: UNW_DEC_REG_PSPREL(P7, UNW_REG_FPSR, t, arg); break; |
| default: UNW_DEC_BAD_CODE(r); break; |
| } |
| } |
| else |
| { |
| switch (code & 0xf) |
| { |
| case 0x0: /* p8 */ |
| { |
| r = *dp++; |
| t = unw_decode_uleb128 (&dp); |
| switch (r) |
| { |
| case 1: UNW_DEC_REG_SPREL(P8, UNW_REG_RP, t, arg); break; |
| case 2: UNW_DEC_REG_SPREL(P8, UNW_REG_PFS, t, arg); break; |
| case 3: UNW_DEC_REG_SPREL(P8, UNW_REG_PR, t, arg); break; |
| case 4: UNW_DEC_REG_SPREL(P8, UNW_REG_LC, t, arg); break; |
| case 5: UNW_DEC_REG_SPREL(P8, UNW_REG_UNAT, t, arg); break; |
| case 6: UNW_DEC_REG_SPREL(P8, UNW_REG_FPSR, t, arg); break; |
| case 7: UNW_DEC_REG_WHEN(P8, UNW_REG_BSP, t, arg); break; |
| case 8: UNW_DEC_REG_PSPREL(P8, UNW_REG_BSP, t, arg); break; |
| case 9: UNW_DEC_REG_SPREL(P8, UNW_REG_BSP, t, arg); break; |
| case 10: UNW_DEC_REG_WHEN(P8, UNW_REG_BSPSTORE, t, arg); break; |
| case 11: UNW_DEC_REG_PSPREL(P8, UNW_REG_BSPSTORE, t, arg); break; |
| case 12: UNW_DEC_REG_SPREL(P8, UNW_REG_BSPSTORE, t, arg); break; |
| case 13: UNW_DEC_REG_WHEN(P8, UNW_REG_RNAT, t, arg); break; |
| case 14: UNW_DEC_REG_PSPREL(P8, UNW_REG_RNAT, t, arg); break; |
| case 15: UNW_DEC_REG_SPREL(P8, UNW_REG_RNAT, t, arg); break; |
| case 16: UNW_DEC_PRIUNAT_WHEN_GR(P8, t, arg); break; |
| case 17: UNW_DEC_PRIUNAT_PSPREL(P8, t, arg); break; |
| case 18: UNW_DEC_PRIUNAT_SPREL(P8, t, arg); break; |
| case 19: UNW_DEC_PRIUNAT_WHEN_MEM(P8, t, arg); break; |
| default: UNW_DEC_BAD_CODE(r); break; |
| } |
| } |
| break; |
| |
| case 0x1: |
| byte1 = *dp++; byte2 = *dp++; |
| UNW_DEC_GR_GR(P9, (byte1 & 0xf), (byte2 & 0x7f), arg); |
| break; |
| |
| case 0xf: /* p10 */ |
| byte1 = *dp++; byte2 = *dp++; |
| UNW_DEC_ABI(P10, byte1, byte2, arg); |
| break; |
| |
| case 0x9: |
| return unw_decode_x1 (dp, code, arg); |
| |
| case 0xa: |
| return unw_decode_x2 (dp, code, arg); |
| |
| case 0xb: |
| return unw_decode_x3 (dp, code, arg); |
| |
| case 0xc: |
| return unw_decode_x4 (dp, code, arg); |
| |
| default: |
| UNW_DEC_BAD_CODE(code); |
| break; |
| } |
| } |
| return dp; |
| } |
| |
| static unsigned char * |
| unw_decode_b1 (unsigned char *dp, unsigned char code, void *arg) |
| { |
| unw_word label = (code & 0x1f); |
| |
| if ((code & 0x20) != 0) |
| UNW_DEC_COPY_STATE(B1, label, arg); |
| else |
| UNW_DEC_LABEL_STATE(B1, label, arg); |
| return dp; |
| } |
| |
| static unsigned char * |
| unw_decode_b2 (unsigned char *dp, unsigned char code, void *arg) |
| { |
| unw_word t; |
| |
| t = unw_decode_uleb128 (&dp); |
| UNW_DEC_EPILOGUE(B2, t, (code & 0x1f), arg); |
| return dp; |
| } |
| |
| static unsigned char * |
| unw_decode_b3_x4 (unsigned char *dp, unsigned char code, void *arg) |
| { |
| unw_word t, ecount, label; |
| |
| if ((code & 0x10) == 0) |
| { |
| t = unw_decode_uleb128 (&dp); |
| ecount = unw_decode_uleb128 (&dp); |
| UNW_DEC_EPILOGUE(B3, t, ecount, arg); |
| } |
| else if ((code & 0x07) == 0) |
| { |
| label = unw_decode_uleb128 (&dp); |
| if ((code & 0x08) != 0) |
| UNW_DEC_COPY_STATE(B4, label, arg); |
| else |
| UNW_DEC_LABEL_STATE(B4, label, arg); |
| } |
| else |
| switch (code & 0x7) |
| { |
| case 1: return unw_decode_x1 (dp, code, arg); |
| case 2: return unw_decode_x2 (dp, code, arg); |
| case 3: return unw_decode_x3 (dp, code, arg); |
| case 4: return unw_decode_x4 (dp, code, arg); |
| default: UNW_DEC_BAD_CODE(code); break; |
| } |
| return dp; |
| } |
| |
| typedef unsigned char *(*unw_decoder) (unsigned char *, unsigned char, void *); |
| |
| static const unw_decoder unw_decode_table[2][8] = |
| { |
| /* prologue table: */ |
| { |
| unw_decode_r1, /* 0 */ |
| unw_decode_r1, |
| unw_decode_r2, |
| unw_decode_r3, |
| unw_decode_p1, /* 4 */ |
| unw_decode_p2_p5, |
| unw_decode_p6, |
| unw_decode_p7_p10 |
| }, |
| { |
| unw_decode_r1, /* 0 */ |
| unw_decode_r1, |
| unw_decode_r2, |
| unw_decode_r3, |
| unw_decode_b1, /* 4 */ |
| unw_decode_b1, |
| unw_decode_b2, |
| unw_decode_b3_x4 |
| } |
| }; |
| |
| /* |
| * Decode one descriptor and return address of next descriptor. |
| */ |
| static inline unsigned char * |
| unw_decode (unsigned char *dp, int inside_body, void *arg) |
| { |
| unw_decoder decoder; |
| unsigned char code; |
| |
| code = *dp++; |
| decoder = unw_decode_table[inside_body][code >> 5]; |
| dp = (*decoder) (dp, code, arg); |
| return dp; |
| } |
| |
| |
| /* RSE helper functions. */ |
| |
| static inline unw_word |
| ia64_rse_slot_num (unw_word *addr) |
| { |
| return (((unw_word) addr) >> 3) & 0x3f; |
| } |
| |
| /* Return TRUE if ADDR is the address of an RNAT slot. */ |
| static inline unw_word |
| ia64_rse_is_rnat_slot (unw_word *addr) |
| { |
| return ia64_rse_slot_num (addr) == 0x3f; |
| } |
| |
| /* Returns the address of the RNAT slot that covers the slot at |
| address SLOT_ADDR. */ |
| static inline unw_word * |
| ia64_rse_rnat_addr (unw_word *slot_addr) |
| { |
| return (unw_word *) ((unw_word) slot_addr | (0x3f << 3)); |
| } |
| |
| /* Calculate the number of registers in the dirty partition starting at |
| BSPSTORE with a size of DIRTY bytes. This isn't simply DIRTY |
| divided by eight because the 64th slot is used to store ar.rnat. */ |
| static inline unw_word |
| ia64_rse_num_regs (unw_word *bspstore, unw_word *bsp) |
| { |
| unw_word slots = (bsp - bspstore); |
| |
| return slots - (ia64_rse_slot_num (bspstore) + slots)/0x40; |
| } |
| |
| /* The inverse of the above: given bspstore and the number of |
| registers, calculate ar.bsp. */ |
| static inline unw_word * |
| ia64_rse_skip_regs (unw_word *addr, int num_regs) |
| { |
| int delta = ia64_rse_slot_num (addr) + num_regs; |
| |
| if (num_regs < 0) |
| delta -= 0x3e; |
| return addr + num_regs + delta/0x3f; |
| } |
| |
| |
| /* Copy register backing store from SRC to DST, LEN words |
| (which include both saved registers and nat collections). |
| DST_RNAT is a partial nat collection for DST. SRC and DST |
| don't have to be equal modulo 64 slots, so it cannot be |
| done with a simple memcpy as the nat collections will be |
| at different relative offsets and need to be combined together. */ |
| static void |
| ia64_copy_rbs (struct _Unwind_Context *info, unw_word dst, |
| unw_word src, unw_word len, unw_word dst_rnat) |
| { |
| unw_word count; |
| unw_word src_rnat; |
| unw_word shift1, shift2; |
| |
| len <<= 3; |
| dst_rnat &= (1ULL << ((dst >> 3) & 0x3f)) - 1; |
| src_rnat = src >= info->regstk_top |
| ? info->rnat : *(unw_word *) (src | 0x1f8); |
| src_rnat &= ~((1ULL << ((src >> 3) & 0x3f)) - 1); |
| /* Just to make sure. */ |
| src_rnat &= ~(1ULL << 63); |
| shift1 = ((dst - src) >> 3) & 0x3f; |
| if ((dst & 0x1f8) < (src & 0x1f8)) |
| shift1--; |
| shift2 = 0x3f - shift1; |
| if ((dst & 0x1f8) >= (src & 0x1f8)) |
| { |
| count = ~dst & 0x1f8; |
| goto first; |
| } |
| count = ~src & 0x1f8; |
| goto second; |
| while (len > 0) |
| { |
| src_rnat = src >= info->regstk_top |
| ? info->rnat : *(unw_word *) (src | 0x1f8); |
| /* Just to make sure. */ |
| src_rnat &= ~(1ULL << 63); |
| count = shift2 << 3; |
| first: |
| if (count > len) |
| count = len; |
| memcpy ((char *) dst, (char *) src, count); |
| dst += count; |
| src += count; |
| len -= count; |
| dst_rnat |= (src_rnat << shift1) & ~(1ULL << 63); |
| if (len <= 0) |
| break; |
| *(unw_word *) dst = dst_rnat; |
| dst += 8; |
| dst_rnat = 0; |
| count = shift1 << 3; |
| second: |
| if (count > len) |
| count = len; |
| memcpy ((char *) dst, (char *) src, count); |
| dst += count; |
| src += count + 8; |
| len -= count + 8; |
| dst_rnat |= (src_rnat >> shift2); |
| } |
| if ((dst & 0x1f8) == 0x1f8) |
| { |
| *(unw_word *) dst = dst_rnat; |
| dst += 8; |
| dst_rnat = 0; |
| } |
| /* Set info->regstk_top to lowest rbs address which will use |
| info->rnat collection. */ |
| info->regstk_top = dst & ~0x1ffULL; |
| info->rnat = dst_rnat; |
| } |
| |
| /* Unwind accessors. */ |
| |
| static void |
| unw_access_gr (struct _Unwind_Context *info, int regnum, |
| unw_word *val, char *nat, int write) |
| { |
| unw_word *addr, *nat_addr = 0, nat_mask = 0, dummy_nat; |
| struct unw_ireg *ireg; |
| |
| if ((unsigned) regnum - 1 >= 127) |
| abort (); |
| |
| if (regnum < 1) |
| { |
| nat_addr = addr = &dummy_nat; |
| dummy_nat = 0; |
| } |
| else if (regnum < 32) |
| { |
| /* Access a non-stacked register. */ |
| ireg = &info->ireg[regnum - 2]; |
| addr = ireg->loc; |
| if (addr) |
| { |
| nat_addr = addr + ireg->nat.off; |
| switch (ireg->nat.type) |
| { |
| case UNW_NAT_VAL: |
| /* Simulate getf.sig/setf.sig. */ |
| if (write) |
| { |
| if (*nat) |
| { |
| /* Write NaTVal and be done with it. */ |
| addr[0] = 0; |
| addr[1] = 0x1fffe; |
| return; |
| } |
| addr[1] = 0x1003e; |
| } |
| else if (addr[0] == 0 && addr[1] == 0x1ffe) |
| { |
| /* Return NaT and be done with it. */ |
| *val = 0; |
| *nat = 1; |
| return; |
| } |
| /* FALLTHRU */ |
| |
| case UNW_NAT_NONE: |
| dummy_nat = 0; |
| nat_addr = &dummy_nat; |
| break; |
| |
| case UNW_NAT_MEMSTK: |
| nat_mask = 1UL << ((unw_word) addr & 0x1f8)/8; |
| break; |
| |
| case UNW_NAT_REGSTK: |
| if ((unw_word) addr >= info->regstk_top) |
| nat_addr = &info->rnat; |
| else |
| nat_addr = ia64_rse_rnat_addr (addr); |
| nat_mask = 1ULL << ia64_rse_slot_num (addr); |
| break; |
| } |
| } |
| } |
| else |
| { |
| /* Access a stacked register. */ |
| addr = ia64_rse_skip_regs ((unw_word *) info->bsp, regnum - 32); |
| if ((unw_word) addr >= info->regstk_top) |
| nat_addr = &info->rnat; |
| else |
| nat_addr = ia64_rse_rnat_addr (addr); |
| nat_mask = 1UL << ia64_rse_slot_num (addr); |
| } |
| |
| if (write) |
| { |
| *addr = *val; |
| if (*nat) |
| *nat_addr |= nat_mask; |
| else |
| *nat_addr &= ~nat_mask; |
| } |
| else |
| { |
| *val = *addr; |
| *nat = (*nat_addr & nat_mask) != 0; |
| } |
| } |
| |
| /* Get the value of register REG as saved in CONTEXT. */ |
| |
| _Unwind_Word |
| _Unwind_GetGR (struct _Unwind_Context *context, int index) |
| { |
| _Unwind_Word ret; |
| char nat; |
| |
| if (index == 1) |
| return context->gp; |
| else if (index >= 15 && index <= 18) |
| return context->eh_data[index - 15]; |
| else |
| unw_access_gr (context, index, &ret, &nat, 0); |
| |
| return ret; |
| } |
| |
| /* Overwrite the saved value for register REG in CONTEXT with VAL. */ |
| |
| void |
| _Unwind_SetGR (struct _Unwind_Context *context, int index, _Unwind_Word val) |
| { |
| char nat = 0; |
| |
| if (index == 1) |
| context->gp = val; |
| else if (index >= 15 && index <= 18) |
| context->eh_data[index - 15] = val; |
| else |
| unw_access_gr (context, index, &val, &nat, 1); |
| } |
| |
| /* Retrieve the return address for CONTEXT. */ |
| |
| inline _Unwind_Ptr |
| _Unwind_GetIP (struct _Unwind_Context *context) |
| { |
| return context->rp; |
| } |
| |
| inline _Unwind_Ptr |
| _Unwind_GetIPInfo (struct _Unwind_Context *context, int *ip_before_insn) |
| { |
| *ip_before_insn = 0; |
| return context->rp; |
| } |
| |
| /* Overwrite the return address for CONTEXT with VAL. */ |
| |
| inline void |
| _Unwind_SetIP (struct _Unwind_Context *context, _Unwind_Ptr val) |
| { |
| context->rp = val; |
| } |
| |
| void * |
| _Unwind_GetLanguageSpecificData (struct _Unwind_Context *context) |
| { |
| return context->lsda; |
| } |
| |
| _Unwind_Ptr |
| _Unwind_GetRegionStart (struct _Unwind_Context *context) |
| { |
| return context->region_start; |
| } |
| |
| void * |
| _Unwind_FindEnclosingFunction (void *pc) |
| { |
| struct unw_table_entry *entp, ent; |
| unw_word segment_base, gp; |
| |
| entp = _Unwind_FindTableEntry (pc, &segment_base, &gp, &ent); |
| if (entp == NULL) |
| return NULL; |
| else |
| return (void *)(segment_base + entp->start_offset); |
| } |
| |
| /* Get the value of the CFA as saved in CONTEXT. In GCC/Dwarf2 parlance, |
| the CFA is the value of the stack pointer on entry; In IA-64 unwind |
| parlance, this is the PSP. */ |
| |
| _Unwind_Word |
| _Unwind_GetCFA (struct _Unwind_Context *context) |
| { |
| return (_Unwind_Ptr) context->psp; |
| } |
| |
| /* Get the value of the Backing Store Pointer as saved in CONTEXT. */ |
| |
| _Unwind_Word |
| _Unwind_GetBSP (struct _Unwind_Context *context) |
| { |
| return (_Unwind_Ptr) context->bsp; |
| } |
| |
| #include "md-unwind-support.h" |
| |
| /* By default, assume personality routine interface compatibility with |
| our expectations. */ |
| #ifndef MD_UNW_COMPATIBLE_PERSONALITY_P |
| #define MD_UNW_COMPATIBLE_PERSONALITY_P(HEADER) 1 |
| #endif |
| |
| |
| static _Unwind_Reason_Code |
| uw_frame_state_for (struct _Unwind_Context *context, _Unwind_FrameState *fs) |
| { |
| struct unw_table_entry *entp, ent; |
| unw_word *unw, header, length; |
| unsigned char *insn, *insn_end; |
| unw_word segment_base; |
| struct unw_reg_info *r; |
| |
| memset (fs, 0, sizeof (*fs)); |
| for (r = fs->curr.reg; r < fs->curr.reg + UNW_NUM_REGS; ++r) |
| r->when = UNW_WHEN_NEVER; |
| context->lsda = 0; |
| |
| entp = _Unwind_FindTableEntry ((void *) context->rp, |
| &segment_base, &context->gp, &ent); |
| if (entp == NULL) |
| { |
| /* Couldn't find unwind info for this function. Try an |
| os-specific fallback mechanism. This will necessarily |
| not provide a personality routine or LSDA. */ |
| #ifdef MD_FALLBACK_FRAME_STATE_FOR |
| if (MD_FALLBACK_FRAME_STATE_FOR (context, fs) == _URC_NO_REASON) |
| return _URC_NO_REASON; |
| #endif |
| |
| /* [SCRA 11.4.1] A leaf function with no memory stack, no exception |
| handlers, and which keeps the return value in B0 does not need |
| an unwind table entry. |
| |
| This can only happen in the frame after unwinding through a signal |
| handler. Avoid infinite looping by requiring that B0 != RP. |
| RP == 0 terminates the chain. */ |
| if (context->br_loc[0] |
| && *context->br_loc[0] != context->rp |
| && context->rp != 0) |
| goto skip_unwind_info; |
| |
| return _URC_END_OF_STACK; |
| } |
| |
| context->region_start = entp->start_offset + segment_base; |
| fs->when_target = ((context->rp & -16) - context->region_start) / 16 * 3 |
| + (context->rp & 15); |
| |
| unw = (unw_word *) (entp->info_offset + segment_base); |
| header = *unw; |
| length = UNW_LENGTH (header); |
| |
| /* Some operating systems use the personality routine slot in way not |
| compatible with what we expect. For instance, OpenVMS uses this slot to |
| designate "condition handlers" with very different arguments than what we |
| would be providing. Such cases are typically identified from OS specific |
| bits in the unwind information block header, and checked by the target |
| MD_UNW_COMPATIBLE_PERSONALITY_P macro. |
| |
| We just pretend there is no personality from our standpoint in such |
| situations, and expect GCC not to set the identifying bits itself so that |
| compatible personalities for GCC compiled code are called. |
| |
| Of course, this raises the question of what combinations of native/GCC |
| calls can be expected to behave properly exception handling-wise. We are |
| not to provide a magic answer here, merely to prevent crashes assuming |
| users know what they are doing. |
| |
| ??? Perhaps check UNW_VER / UNW_FLAG_OSMASK as well. */ |
| |
| if (MD_UNW_COMPATIBLE_PERSONALITY_P (header) |
| && (UNW_FLAG_EHANDLER (header) | UNW_FLAG_UHANDLER (header))) |
| { |
| fs->personality = |
| *(_Unwind_Personality_Fn *) (unw[length + 1] + context->gp); |
| context->lsda = unw + length + 2; |
| } |
| |
| insn = (unsigned char *) (unw + 1); |
| insn_end = (unsigned char *) (unw + 1 + length); |
| while (!fs->done && insn < insn_end) |
| insn = unw_decode (insn, fs->in_body, fs); |
| |
| free_label_states (fs->labeled_states); |
| free_state_stack (&fs->curr); |
| |
| #ifdef ENABLE_MALLOC_CHECKING |
| if (reg_state_alloced || labeled_state_alloced) |
| abort (); |
| #endif |
| |
| /* If we're in the epilogue, sp has been restored and all values |
| on the memory stack below psp also have been restored. */ |
| if (fs->when_target > fs->epilogue_start) |
| { |
| struct unw_reg_info *r; |
| |
| fs->curr.reg[UNW_REG_PSP].where = UNW_WHERE_NONE; |
| fs->curr.reg[UNW_REG_PSP].val = 0; |
| for (r = fs->curr.reg; r < fs->curr.reg + UNW_NUM_REGS; ++r) |
| if ((r->where == UNW_WHERE_PSPREL && r->val <= 0x10) |
| || r->where == UNW_WHERE_SPREL) |
| r->where = UNW_WHERE_NONE; |
| } |
| |
| skip_unwind_info: |
| /* If RP didn't get saved, generate entry for the return link register. */ |
| if (fs->curr.reg[UNW_REG_RP].when >= fs->when_target) |
| { |
| fs->curr.reg[UNW_REG_RP].where = UNW_WHERE_BR; |
| fs->curr.reg[UNW_REG_RP].when = -1; |
| fs->curr.reg[UNW_REG_RP].val = fs->return_link_reg; |
| } |
| |
| /* There is a subtlety for the frame after unwinding through a signal |
| handler: should we restore the cfm as usual or the pfs? We can't |
| restore both because we use br.ret to resume execution of user code. |
| For other frames the procedure is by definition non-leaf so the pfs |
| is saved and restored and thus effectively dead in the body; only |
| the cfm need therefore be restored. |
| |
| Here we have 2 cases: |
| - either the pfs is saved and restored and thus effectively dead |
| like in regular frames; then we do nothing special and restore |
| the cfm. |
| - or the pfs is not saved and thus live; but in that case the |
| procedure is necessarily leaf so the cfm is effectively dead |
| and we restore the pfs. */ |
| if (context->signal_pfs_loc) |
| { |
| if (fs->curr.reg[UNW_REG_PFS].when >= fs->when_target) |
| context->pfs_loc = context->signal_pfs_loc; |
| context->signal_pfs_loc = NULL; |
| } |
| |
| return _URC_NO_REASON; |
| } |
| |
| static void |
| uw_update_reg_address (struct _Unwind_Context *context, |
| _Unwind_FrameState *fs, |
| enum unw_register_index regno) |
| { |
| struct unw_reg_info *r = fs->curr.reg + regno; |
| void *addr; |
| unw_word rval; |
| |
| if (r->where == UNW_WHERE_NONE || r->when >= fs->when_target) |
| return; |
| |
| rval = r->val; |
| switch (r->where) |
| { |
| case UNW_WHERE_GR: |
| if (rval >= 32) |
| addr = ia64_rse_skip_regs ((unw_word *) context->bsp, rval - 32); |
| else if (rval >= 2) |
| addr = context->ireg[rval - 2].loc; |
| else if (rval == 0) |
| { |
| static const unw_word dummy; |
| addr = (void *) &dummy; |
| } |
| else |
| abort (); |
| break; |
| |
| case UNW_WHERE_FR: |
| if (rval >= 2 && rval < 32) |
| addr = context->fr_loc[rval - 2]; |
| else |
| abort (); |
| break; |
| |
| case UNW_WHERE_BR: |
| /* Note that while RVAL can only be 1-5 from normal descriptors, |
| we can want to look at B0, B6 and B7 due to having manually unwound a |
| signal frame. */ |
| if (rval < 8) |
| addr = context->br_loc[rval]; |
| else |
| abort (); |
| break; |
| |
| case UNW_WHERE_SPREL: |
| addr = (void *)(context->sp + rval); |
| break; |
| |
| case UNW_WHERE_PSPREL: |
| addr = (void *)(context->psp + rval); |
| break; |
| |
| default: |
| abort (); |
| } |
| |
| switch (regno) |
| { |
| case UNW_REG_R2 ... UNW_REG_R31: |
| context->ireg[regno - UNW_REG_R2].loc = addr; |
| switch (r->where) |
| { |
| case UNW_WHERE_GR: |
| if (rval >= 32) |
| { |
| context->ireg[regno - UNW_REG_R2].nat.type = UNW_NAT_MEMSTK; |
| context->ireg[regno - UNW_REG_R2].nat.off |
| = context->pri_unat_loc - (unw_word *) addr; |
| } |
| else if (rval >= 2) |
| { |
| context->ireg[regno - UNW_REG_R2].nat |
| = context->ireg[rval - 2].nat; |
| } |
| else if (rval == 0) |
| { |
| context->ireg[regno - UNW_REG_R2].nat.type = UNW_NAT_NONE; |
| context->ireg[regno - UNW_REG_R2].nat.off = 0; |
| } |
| else |
| abort (); |
| break; |
| |
| case UNW_WHERE_FR: |
| context->ireg[regno - UNW_REG_R2].nat.type = UNW_NAT_VAL; |
| context->ireg[regno - UNW_REG_R2].nat.off = 0; |
| break; |
| |
| case UNW_WHERE_BR: |
| context->ireg[regno - UNW_REG_R2].nat.type = UNW_NAT_NONE; |
| context->ireg[regno - UNW_REG_R2].nat.off = 0; |
| break; |
| |
| case UNW_WHERE_PSPREL: |
| case UNW_WHERE_SPREL: |
| context->ireg[regno - UNW_REG_R2].nat.type = UNW_NAT_MEMSTK; |
| context->ireg[regno - UNW_REG_R2].nat.off |
| = context->pri_unat_loc - (unw_word *) addr; |
| break; |
| |
| default: |
| abort (); |
| } |
| break; |
| |
| case UNW_REG_F2 ... UNW_REG_F31: |
| context->fr_loc[regno - UNW_REG_F2] = addr; |
| break; |
| |
| case UNW_REG_B1 ... UNW_REG_B5: |
| context->br_loc[regno - UNW_REG_B0] = addr; |
| break; |
| |
| case UNW_REG_BSP: |
| context->bsp_loc = addr; |
| break; |
| case UNW_REG_BSPSTORE: |
| context->bspstore_loc = addr; |
| break; |
| case UNW_REG_PFS: |
| context->pfs_loc = addr; |
| break; |
| case UNW_REG_RP: |
| context->rp = *(unw_word *)addr; |
| break; |
| case UNW_REG_UNAT: |
| context->unat_loc = addr; |
| break; |
| case UNW_REG_PR: |
| context->pr = *(unw_word *) addr; |
| break; |
| case UNW_REG_LC: |
| context->lc_loc = addr; |
| break; |
| case UNW_REG_FPSR: |
| context->fpsr_loc = addr; |
| break; |
| |
| case UNW_REG_PSP: |
| context->psp = *(unw_word *)addr; |
| break; |
| |
| default: |
| abort (); |
| } |
| } |
| |
| static void |
| uw_update_context (struct _Unwind_Context *context, _Unwind_FrameState *fs) |
| { |
| int i; |
| |
| #ifdef MD_HANDLE_UNWABI |
| MD_HANDLE_UNWABI (context, fs); |
| #endif |
| |
| context->sp = context->psp; |
| |
| /* First, set PSP. Subsequent instructions may depend on this value. */ |
| if (fs->when_target > fs->curr.reg[UNW_REG_PSP].when) |
| { |
| if (fs->curr.reg[UNW_REG_PSP].where == UNW_WHERE_NONE) |
| context->psp = context->psp + fs->curr.reg[UNW_REG_PSP].val; |
| else |
| uw_update_reg_address (context, fs, UNW_REG_PSP); |
| } |
| |
| /* Determine the location of the primary UNaT. */ |
| { |
| int i; |
| if (fs->when_target < fs->curr.reg[UNW_REG_PRI_UNAT_GR].when) |
| i = UNW_REG_PRI_UNAT_MEM; |
| else if (fs->when_target < fs->curr.reg[UNW_REG_PRI_UNAT_MEM].when) |
| i = UNW_REG_PRI_UNAT_GR; |
| else if (fs->curr.reg[UNW_REG_PRI_UNAT_MEM].when |
| > fs->curr.reg[UNW_REG_PRI_UNAT_GR].when) |
| i = UNW_REG_PRI_UNAT_MEM; |
| else |
| i = UNW_REG_PRI_UNAT_GR; |
| uw_update_reg_address (context, fs, i); |
| } |
| |
| /* Compute the addresses of all registers saved in this frame. */ |
| for (i = UNW_REG_BSP; i < UNW_NUM_REGS; ++i) |
| uw_update_reg_address (context, fs, i); |
| |
| /* Unwind BSP for the local registers allocated this frame. */ |
| /* ??? What to do with stored BSP or BSPSTORE registers. */ |
| /* We assert that we are either at a call site, or we have |
| just unwound through a signal frame. In either case |
| pfs_loc is valid. */ |
| if (!(fs -> no_reg_stack_frame)) |
| { |
| unw_word pfs = *context->pfs_loc; |
| unw_word sol = (pfs >> 7) & 0x7f; |
| context->bsp = (unw_word) |
| ia64_rse_skip_regs ((unw_word *) context->bsp, -sol); |
| } |
| } |
| |
| static void |
| uw_advance_context (struct _Unwind_Context *context, _Unwind_FrameState *fs) |
| { |
| uw_update_context (context, fs); |
| } |
| |
| /* Fill in CONTEXT for top-of-stack. The only valid registers at this |
| level will be the return address and the CFA. Note that CFA = SP+16. */ |
| |
| #define uw_init_context(CONTEXT) \ |
| do { \ |
| /* ??? There is a whole lot o code in uw_install_context that \ |
| tries to avoid spilling the entire machine state here. We \ |
| should try to make that work again. */ \ |
| __builtin_unwind_init(); \ |
| uw_init_context_1 (CONTEXT, __builtin_ia64_bsp ()); \ |
| } while (0) |
| |
| static void __attribute__((noinline)) |
| uw_init_context_1 (struct _Unwind_Context *context, void *bsp) |
| { |
| void *rp = __builtin_extract_return_addr (__builtin_return_address (0)); |
| /* Set psp to the caller's stack pointer. */ |
| void *psp = __builtin_dwarf_cfa () - 16; |
| _Unwind_FrameState fs; |
| unw_word rnat, tmp1, tmp2; |
| |
| /* Flush the register stack to memory so that we can access it. |
| Get rse nat collection for the last incomplete rbs chunk of |
| registers at the same time. For this RSE needs to be turned |
| into the mandatory only mode. */ |
| asm ("mov.m %1 = ar.rsc;;\n\t" |
| "and %2 = 0x1c, %1;;\n\t" |
| "mov.m ar.rsc = %2;;\n\t" |
| "flushrs;;\n\t" |
| "mov.m %0 = ar.rnat;;\n\t" |
| "mov.m ar.rsc = %1\n\t" |
| : "=r" (rnat), "=r" (tmp1), "=r" (tmp2)); |
| |
| memset (context, 0, sizeof (struct _Unwind_Context)); |
| context->bsp = (unw_word) bsp; |
| /* Set context->regstk_top to lowest rbs address which will use |
| context->rnat collection. */ |
| context->regstk_top = context->bsp & ~0x1ffULL; |
| context->rnat = rnat; |
| context->psp = (unw_word) psp; |
| context->rp = (unw_word) rp; |
| asm ("mov %0 = sp" : "=r" (context->sp)); |
| asm ("mov %0 = pr" : "=r" (context->pr)); |
| context->pri_unat_loc = &context->initial_unat; /* ??? */ |
| |
| if (uw_frame_state_for (context, &fs) != _URC_NO_REASON) |
| abort (); |
| |
| uw_update_context (context, &fs); |
| } |
| |
| /* Install (i.e. longjmp to) the contents of TARGET. */ |
| |
| static void __attribute__((noreturn)) |
| uw_install_context (struct _Unwind_Context *current __attribute__((unused)), |
| struct _Unwind_Context *target, |
| unsigned long frames __attribute__((unused))) |
| { |
| unw_word ireg_buf[4], ireg_nat = 0, ireg_pr = 0; |
| unw_word saved_lc; |
| int i; |
| |
| /* ??? LC is a fixed register so the call to __builtin_unwind_init in |
| uw_init_context doesn't cause it to be saved. In case it isn't in |
| the user frames either, we need to manually do so here, lest it be |
| clobbered by the loop just below. */ |
| if (target->lc_loc == NULL) |
| { |
| register unw_word lc asm ("ar.lc"); |
| saved_lc = lc; |
| target->lc_loc = &saved_lc; |
| } |
| |
| /* Copy integer register data from the target context to a |
| temporary buffer. Do this so that we can frob AR.UNAT |
| to get the NaT bits for these registers set properly. */ |
| for (i = 4; i <= 7; ++i) |
| { |
| char nat; |
| void *t = target->ireg[i - 2].loc; |
| if (t) |
| { |
| unw_access_gr (target, i, &ireg_buf[i - 4], &nat, 0); |
| ireg_nat |= (unw_word)nat << (((size_t)&ireg_buf[i - 4] >> 3) & 0x3f); |
| /* Set p6 - p9. */ |
| ireg_pr |= 4L << i; |
| } |
| } |
| |
| /* The value in uc_bsp that we've computed is that for the |
| target function. The value that we install below will be |
| adjusted by the BR.RET instruction based on the contents |
| of AR.PFS. So we must unadjust that here. */ |
| target->bsp = (unw_word) |
| ia64_rse_skip_regs ((unw_word *)target->bsp, |
| (*target->pfs_loc >> 7) & 0x7f); |
| |
| if (target->bsp < target->regstk_top) |
| target->rnat = *ia64_rse_rnat_addr ((unw_word *) target->bsp); |
| |
| /* Provide assembly with the offsets into the _Unwind_Context. */ |
| asm volatile ("uc_rnat = %0" |
| : : "i"(offsetof (struct _Unwind_Context, rnat))); |
| asm volatile ("uc_bsp = %0" |
| : : "i"(offsetof (struct _Unwind_Context, bsp))); |
| asm volatile ("uc_psp = %0" |
| : : "i"(offsetof (struct _Unwind_Context, psp))); |
| asm volatile ("uc_rp = %0" |
| : : "i"(offsetof (struct _Unwind_Context, rp))); |
| asm volatile ("uc_pr = %0" |
| : : "i"(offsetof (struct _Unwind_Context, pr))); |
| asm volatile ("uc_gp = %0" |
| : : "i"(offsetof (struct _Unwind_Context, gp))); |
| asm volatile ("uc_pfs_loc = %0" |
| : : "i"(offsetof (struct _Unwind_Context, pfs_loc))); |
| asm volatile ("uc_unat_loc = %0" |
| : : "i"(offsetof (struct _Unwind_Context, unat_loc))); |
| asm volatile ("uc_lc_loc = %0" |
| : : "i"(offsetof (struct _Unwind_Context, lc_loc))); |
| asm volatile ("uc_fpsr_loc = %0" |
| : : "i"(offsetof (struct _Unwind_Context, fpsr_loc))); |
| asm volatile ("uc_eh_data = %0" |
| : : "i"(offsetof (struct _Unwind_Context, eh_data))); |
| asm volatile ("uc_br_loc = %0" |
| : : "i"(offsetof (struct _Unwind_Context, br_loc))); |
| asm volatile ("uc_fr_loc = %0" |
| : : "i"(offsetof (struct _Unwind_Context, fr_loc))); |
| |
| asm volatile ( |
| /* Load up call-saved non-window integer registers from ireg_buf. */ |
| "add r20 = 8, %1 \n\t" |
| "mov ar.unat = %2 \n\t" |
| "mov pr = %3, 0x3c0 \n\t" |
| ";; \n\t" |
| "(p6) ld8.fill r4 = [%1] \n\t" |
| "(p7) ld8.fill r5 = [r20] \n\t" |
| "add r21 = uc_br_loc + 16, %0 \n\t" |
| "adds %1 = 16, %1 \n\t" |
| "adds r20 = 16, r20 \n\t" |
| ";; \n\t" |
| "(p8) ld8.fill r6 = [%1] \n\t" |
| "(p9) ld8.fill r7 = [r20] \n\t" |
| "add r20 = uc_br_loc + 8, %0 \n\t" |
| ";; \n\t" |
| /* Load up call-saved branch registers. */ |
| "ld8 r22 = [r20], 16 \n\t" |
| "ld8 r23 = [r21], 16 \n\t" |
| ";; \n\t" |
| "ld8 r24 = [r20], 16 \n\t" |
| "ld8 r25 = [r21], uc_fr_loc - (uc_br_loc + 32)\n\t" |
| ";; \n\t" |
| "ld8 r26 = [r20], uc_fr_loc + 8 - (uc_br_loc + 40)\n\t" |
| "ld8 r27 = [r21], 24 \n\t" |
| "cmp.ne p6, p0 = r0, r22 \n\t" |
| ";; \n\t" |
| "ld8 r28 = [r20], 8 \n\t" |
| "(p6) ld8 r22 = [r22] \n\t" |
| "cmp.ne p7, p0 = r0, r23 \n\t" |
| ";; \n\t" |
| "(p7) ld8 r23 = [r23] \n\t" |
| "cmp.ne p8, p0 = r0, r24 \n\t" |
| ";; \n\t" |
| "(p8) ld8 r24 = [r24] \n\t" |
| "(p6) mov b1 = r22 \n\t" |
| "cmp.ne p9, p0 = r0, r25 \n\t" |
| ";; \n\t" |
| "(p9) ld8 r25 = [r25] \n\t" |
| "(p7) mov b2 = r23 \n\t" |
| "cmp.ne p6, p0 = r0, r26 \n\t" |
| ";; \n\t" |
| "(p6) ld8 r26 = [r26] \n\t" |
| "(p8) mov b3 = r24 \n\t" |
| "cmp.ne p7, p0 = r0, r27 \n\t" |
| ";; \n\t" |
| /* Load up call-saved fp registers. */ |
| "(p7) ldf.fill f2 = [r27] \n\t" |
| "(p9) mov b4 = r25 \n\t" |
| "cmp.ne p8, p0 = r0, r28 \n\t" |
| ";; \n\t" |
| "(p8) ldf.fill f3 = [r28] \n\t" |
| "(p6) mov b5 = r26 \n\t" |
| ";; \n\t" |
| "ld8 r29 = [r20], 16*8 - 4*8 \n\t" |
| "ld8 r30 = [r21], 17*8 - 5*8 \n\t" |
| ";; \n\t" |
| "ld8 r22 = [r20], 16 \n\t" |
| "ld8 r23 = [r21], 16 \n\t" |
| ";; \n\t" |
| "ld8 r24 = [r20], 16 \n\t" |
| "ld8 r25 = [r21] \n\t" |
| "cmp.ne p6, p0 = r0, r29 \n\t" |
| ";; \n\t" |
| "ld8 r26 = [r20], 8 \n\t" |
| "(p6) ldf.fill f4 = [r29] \n\t" |
| "cmp.ne p7, p0 = r0, r30 \n\t" |
| ";; \n\t" |
| "ld8 r27 = [r20], 8 \n\t" |
| "(p7) ldf.fill f5 = [r30] \n\t" |
| "cmp.ne p6, p0 = r0, r22 \n\t" |
| ";; \n\t" |
| "ld8 r28 = [r20], 8 \n\t" |
| "(p6) ldf.fill f16 = [r22] \n\t" |
| "cmp.ne p7, p0 = r0, r23 \n\t" |
| ";; \n\t" |
| "ld8 r29 = [r20], 8 \n\t" |
| "(p7) ldf.fill f17 = [r23] \n\t" |
| "cmp.ne p6, p0 = r0, r24 \n\t" |
| ";; \n\t" |
| "ld8 r22 = [r20], 8 \n\t" |
| "(p6) ldf.fill f18 = [r24] \n\t" |
| "cmp.ne p7, p0 = r0, r25 \n\t" |
| ";; \n\t" |
| "ld8 r23 = [r20], 8 \n\t" |
| "(p7) ldf.fill f19 = [r25] \n\t" |
| "cmp.ne p6, p0 = r0, r26 \n\t" |
| ";; \n\t" |
| "ld8 r24 = [r20], 8 \n\t" |
| "(p6) ldf.fill f20 = [r26] \n\t" |
| "cmp.ne p7, p0 = r0, r27 \n\t" |
| ";; \n\t" |
| "ld8 r25 = [r20], 8 \n\t" |
| "(p7) ldf.fill f21 = [r27] \n\t" |
| "cmp.ne p6, p0 = r0, r28 \n\t" |
| ";; \n\t" |
| "ld8 r26 = [r20], 8 \n\t" |
| "(p6) ldf.fill f22 = [r28] \n\t" |
| "cmp.ne p7, p0 = r0, r29 \n\t" |
| ";; \n\t" |
| "ld8 r27 = [r20], 8 \n\t" |
| ";; \n\t" |
| "ld8 r28 = [r20], 8 \n\t" |
| "(p7) ldf.fill f23 = [r29] \n\t" |
| "cmp.ne p6, p0 = r0, r22 \n\t" |
| ";; \n\t" |
| "ld8 r29 = [r20], 8 \n\t" |
| "(p6) ldf.fill f24 = [r22] \n\t" |
| "cmp.ne p7, p0 = r0, r23 \n\t" |
| ";; \n\t" |
| "(p7) ldf.fill f25 = [r23] \n\t" |
| "cmp.ne p6, p0 = r0, r24 \n\t" |
| "cmp.ne p7, p0 = r0, r25 \n\t" |
| ";; \n\t" |
| "(p6) ldf.fill f26 = [r24] \n\t" |
| "(p7) ldf.fill f27 = [r25] \n\t" |
| "cmp.ne p6, p0 = r0, r26 \n\t" |
| ";; \n\t" |
| "(p6) ldf.fill f28 = [r26] \n\t" |
| "cmp.ne p7, p0 = r0, r27 \n\t" |
| "cmp.ne p6, p0 = r0, r28 \n\t" |
| ";; \n\t" |
| "(p7) ldf.fill f29 = [r27] \n\t" |
| "(p6) ldf.fill f30 = [r28] \n\t" |
| "cmp.ne p7, p0 = r0, r29 \n\t" |
| ";; \n\t" |
| "(p7) ldf.fill f31 = [r29] \n\t" |
| "add r20 = uc_rnat, %0 \n\t" |
| "add r21 = uc_bsp, %0 \n\t" |
| ";; \n\t" |
| /* Load the balance of the thread state from the context. */ |
| "ld8 r22 = [r20], uc_psp - uc_rnat \n\t" |
| "ld8 r23 = [r21], uc_gp - uc_bsp \n\t" |
| ";; \n\t" |
| "ld8 r24 = [r20], uc_pfs_loc - uc_psp \n\t" |
| "ld8 r1 = [r21], uc_rp - uc_gp \n\t" |
| ";; \n\t" |
| "ld8 r25 = [r20], uc_unat_loc - uc_pfs_loc\n\t" |
| "ld8 r26 = [r21], uc_pr - uc_rp \n\t" |
| ";; \n\t" |
| "ld8 r27 = [r20], uc_lc_loc - uc_unat_loc\n\t" |
| "ld8 r28 = [r21], uc_fpsr_loc - uc_pr \n\t" |
| ";; \n\t" |
| "ld8 r29 = [r20], uc_eh_data - uc_lc_loc\n\t" |
| "ld8 r30 = [r21], uc_eh_data + 8 - uc_fpsr_loc\n\t" |
| ";; \n\t" |
| /* Load data for the exception handler. */ |
| "ld8 r15 = [r20], 16 \n\t" |
| "ld8 r16 = [r21], 16 \n\t" |
| ";; \n\t" |
| "ld8 r17 = [r20] \n\t" |
| "ld8 r18 = [r21] \n\t" |
| ";; \n\t" |
| /* Install the balance of the thread state loaded above. */ |
| "cmp.ne p6, p0 = r0, r25 \n\t" |
| "cmp.ne p7, p0 = r0, r27 \n\t" |
| ";; \n\t" |
| "(p6) ld8 r25 = [r25] \n\t" |
| "(p7) ld8 r27 = [r27] \n\t" |
| ";; \n\t" |
| "(p7) mov.m ar.unat = r27 \n\t" |
| "(p6) mov.i ar.pfs = r25 \n\t" |
| "cmp.ne p9, p0 = r0, r29 \n\t" |
| ";; \n\t" |
| "(p9) ld8 r29 = [r29] \n\t" |
| "cmp.ne p6, p0 = r0, r30 \n\t" |
| ";; \n\t" |
| "(p6) ld8 r30 = [r30] \n\t" |
| /* Don't clobber p6-p9, which are in use at present. */ |
| "mov pr = r28, ~0x3c0 \n\t" |
| "(p9) mov.i ar.lc = r29 \n\t" |
| ";; \n\t" |
| "mov.m r25 = ar.rsc \n\t" |
| "(p6) mov.m ar.fpsr = r30 \n\t" |
| ";; \n\t" |
| "and r29 = 0x1c, r25 \n\t" |
| "mov b0 = r26 \n\t" |
| ";; \n\t" |
| "mov.m ar.rsc = r29 \n\t" |
| ";; \n\t" |
| /* This must be done before setting AR.BSPSTORE, otherwise |
| AR.BSP will be initialized with a random displacement |
| below the value we want, based on the current number of |
| dirty stacked registers. */ |
| "loadrs \n\t" |
| "invala \n\t" |
| ";; \n\t" |
| "mov.m ar.bspstore = r23 \n\t" |
| ";; \n\t" |
| "mov.m ar.rnat = r22 \n\t" |
| ";; \n\t" |
| "mov.m ar.rsc = r25 \n\t" |
| "mov sp = r24 \n\t" |
| "br.ret.sptk.few b0" |
| : : "r"(target), "r"(ireg_buf), "r"(ireg_nat), "r"(ireg_pr) |
| : "r15", "r16", "r17", "r18", "r20", "r21", "r22", |
| "r23", "r24", "r25", "r26", "r27", "r28", "r29", |
| "r30", "r31"); |
| /* NOTREACHED */ |
| while (1); |
| } |
| |
| static inline _Unwind_Ptr |
| uw_identify_context (struct _Unwind_Context *context) |
| { |
| return _Unwind_GetIP (context); |
| } |
| |
| #include "unwind.inc" |
| |
| #if defined (USE_GAS_SYMVER) && defined (SHARED) && defined (USE_LIBUNWIND_EXCEPTIONS) |
| alias (_Unwind_Backtrace); |
| alias (_Unwind_DeleteException); |
| alias (_Unwind_FindEnclosingFunction); |
| alias (_Unwind_ForcedUnwind); |
| alias (_Unwind_GetBSP); |
| alias (_Unwind_GetCFA); |
| alias (_Unwind_GetGR); |
| alias (_Unwind_GetIP); |
| alias (_Unwind_GetLanguageSpecificData); |
| alias (_Unwind_GetRegionStart); |
| alias (_Unwind_RaiseException); |
| alias (_Unwind_Resume); |
| alias (_Unwind_Resume_or_Rethrow); |
| alias (_Unwind_SetGR); |
| alias (_Unwind_SetIP); |
| #endif |
| |
| #endif |