Home | History | Annotate | Download | only in ia64
      1 /* libunwind - a platform-independent unwind library
      2    Copyright (C) 2001-2005 Hewlett-Packard Co
      3 	Contributed by David Mosberger-Tang <davidm (at) hpl.hp.com>
      4 
      5 This file is part of libunwind.
      6 
      7 Permission is hereby granted, free of charge, to any person obtaining
      8 a copy of this software and associated documentation files (the
      9 "Software"), to deal in the Software without restriction, including
     10 without limitation the rights to use, copy, modify, merge, publish,
     11 distribute, sublicense, and/or sell copies of the Software, and to
     12 permit persons to whom the Software is furnished to do so, subject to
     13 the following conditions:
     14 
     15 The above copyright notice and this permission notice shall be
     16 included in all copies or substantial portions of the Software.
     17 
     18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     19 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     20 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     21 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     22 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     23 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     24 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
     25 
     26 #include "offsets.h"
     27 #include "regs.h"
     28 #include "unwind_i.h"
     29 
     30 static inline ia64_loc_t
     31 linux_scratch_loc (struct cursor *c, unw_regnum_t reg, uint8_t *nat_bitnr)
     32 {
     33 #if !defined(UNW_LOCAL_ONLY) || defined(__linux)
     34   unw_word_t addr = c->sigcontext_addr, flags, tmp_addr;
     35   int i;
     36 
     37   if (ia64_get_abi_marker (c) == ABI_MARKER_LINUX_SIGTRAMP
     38       || ia64_get_abi_marker (c) == ABI_MARKER_OLD_LINUX_SIGTRAMP)
     39     {
     40       switch (reg)
     41 	{
     42 	case UNW_IA64_NAT + 2 ... UNW_IA64_NAT + 3:
     43 	case UNW_IA64_NAT + 8 ... UNW_IA64_NAT + 31:
     44 	  /* Linux sigcontext contains the NaT bit of scratch register
     45 	     N in bit position N of the sc_nat member. */
     46 	  *nat_bitnr = (reg - UNW_IA64_NAT);
     47 	  addr += LINUX_SC_NAT_OFF;
     48 	  break;
     49 
     50 	case UNW_IA64_GR +  2 ... UNW_IA64_GR + 3:
     51 	case UNW_IA64_GR +  8 ... UNW_IA64_GR + 31:
     52 	  addr += LINUX_SC_GR_OFF + 8 * (reg - UNW_IA64_GR);
     53 	  break;
     54 
     55 	case UNW_IA64_FR + 6 ... UNW_IA64_FR + 15:
     56 	  addr += LINUX_SC_FR_OFF + 16 * (reg - UNW_IA64_FR);
     57 	  return IA64_LOC_ADDR (addr, IA64_LOC_TYPE_FP);
     58 
     59 	case UNW_IA64_FR + 32 ... UNW_IA64_FR + 127:
     60 	  if (ia64_get (c, IA64_LOC_ADDR (addr + LINUX_SC_FLAGS_OFF, 0),
     61 			&flags) < 0)
     62 	    return IA64_NULL_LOC;
     63 
     64 	  if (!(flags & IA64_SC_FLAG_FPH_VALID))
     65 	    {
     66 	      /* initialize fph partition: */
     67 	      tmp_addr = addr + LINUX_SC_FR_OFF + 32*16;
     68 	      for (i = 32; i < 128; ++i, tmp_addr += 16)
     69 		if (ia64_putfp (c, IA64_LOC_ADDR (tmp_addr, 0),
     70 				unw.read_only.f0) < 0)
     71 		  return IA64_NULL_LOC;
     72 	      /* mark fph partition as valid: */
     73 	      if (ia64_put (c, IA64_LOC_ADDR (addr + LINUX_SC_FLAGS_OFF, 0),
     74 			    flags | IA64_SC_FLAG_FPH_VALID) < 0)
     75 		return IA64_NULL_LOC;
     76 	    }
     77 
     78 	  addr += LINUX_SC_FR_OFF + 16 * (reg - UNW_IA64_FR);
     79 	  return IA64_LOC_ADDR (addr, IA64_LOC_TYPE_FP);
     80 
     81 	case UNW_IA64_BR + 0: addr += LINUX_SC_BR_OFF + 0; break;
     82 	case UNW_IA64_BR + 6: addr += LINUX_SC_BR_OFF + 6*8; break;
     83 	case UNW_IA64_BR + 7: addr += LINUX_SC_BR_OFF + 7*8; break;
     84 	case UNW_IA64_AR_RSC: addr += LINUX_SC_AR_RSC_OFF; break;
     85 	case UNW_IA64_AR_CSD: addr += LINUX_SC_AR_CSD_OFF; break;
     86 	case UNW_IA64_AR_SSD: addr += LINUX_SC_AR_SSD_OFF; break;
     87 	case UNW_IA64_AR_CCV: addr += LINUX_SC_AR_CCV; break;
     88 
     89 	default:
     90 	  if (unw_is_fpreg (reg))
     91 	    return IA64_FPREG_LOC (c, reg);
     92 	  else
     93 	    return IA64_REG_LOC (c, reg);
     94 	}
     95       return IA64_LOC_ADDR (addr, 0);
     96     }
     97   else
     98     {
     99       int is_nat = 0;
    100 
    101       if ((unsigned) (reg - UNW_IA64_NAT) < 128)
    102 	{
    103 	  is_nat = 1;
    104 	  reg -= (UNW_IA64_NAT - UNW_IA64_GR);
    105 	}
    106       if (ia64_get_abi_marker (c) == ABI_MARKER_LINUX_INTERRUPT)
    107 	{
    108 	  switch (reg)
    109 	    {
    110 	    case UNW_IA64_BR + 6 ... UNW_IA64_BR + 7:
    111 	      addr += LINUX_PT_B6_OFF + 8 * (reg - (UNW_IA64_BR + 6));
    112 	      break;
    113 
    114 	    case UNW_IA64_AR_CSD: addr += LINUX_PT_CSD_OFF; break;
    115 	    case UNW_IA64_AR_SSD: addr += LINUX_PT_SSD_OFF; break;
    116 
    117 	    case UNW_IA64_GR +  8 ... UNW_IA64_GR + 11:
    118 	      addr += LINUX_PT_R8_OFF + 8 * (reg - (UNW_IA64_GR + 8));
    119 	      break;
    120 
    121 	    case UNW_IA64_IP: addr += LINUX_PT_IIP_OFF; break;
    122 	    case UNW_IA64_CFM: addr += LINUX_PT_IFS_OFF; break;
    123 	    case UNW_IA64_AR_UNAT: addr += LINUX_PT_UNAT_OFF; break;
    124 	    case UNW_IA64_AR_PFS: addr += LINUX_PT_PFS_OFF; break;
    125 	    case UNW_IA64_AR_RSC: addr += LINUX_PT_RSC_OFF; break;
    126 	    case UNW_IA64_AR_RNAT: addr += LINUX_PT_RNAT_OFF; break;
    127 	    case UNW_IA64_AR_BSPSTORE: addr += LINUX_PT_BSPSTORE_OFF; break;
    128 	    case UNW_IA64_PR: addr += LINUX_PT_PR_OFF; break;
    129 	    case UNW_IA64_BR + 0: addr += LINUX_PT_B0_OFF; break;
    130 
    131 	    case UNW_IA64_GR + 1:
    132 	      /* The saved r1 value is valid only in the frame in which
    133 		 it was saved; for everything else we need to look up
    134 		 the appropriate gp value.  */
    135 	      if (c->sigcontext_addr != c->sp + 0x10)
    136 		return IA64_NULL_LOC;
    137 	      addr += LINUX_PT_R1_OFF;
    138 	      break;
    139 
    140 	    case UNW_IA64_GR + 12: addr += LINUX_PT_R12_OFF; break;
    141 	    case UNW_IA64_GR + 13: addr += LINUX_PT_R13_OFF; break;
    142 	    case UNW_IA64_AR_FPSR: addr += LINUX_PT_FPSR_OFF; break;
    143 	    case UNW_IA64_GR + 15: addr += LINUX_PT_R15_OFF; break;
    144 	    case UNW_IA64_GR + 14: addr += LINUX_PT_R14_OFF; break;
    145 	    case UNW_IA64_GR + 2: addr += LINUX_PT_R2_OFF; break;
    146 	    case UNW_IA64_GR + 3: addr += LINUX_PT_R3_OFF; break;
    147 
    148 	    case UNW_IA64_GR + 16 ... UNW_IA64_GR + 31:
    149 	      addr += LINUX_PT_R16_OFF + 8 * (reg - (UNW_IA64_GR + 16));
    150 	      break;
    151 
    152 	    case UNW_IA64_AR_CCV: addr += LINUX_PT_CCV_OFF; break;
    153 
    154 	    case UNW_IA64_FR + 6 ... UNW_IA64_FR + 11:
    155 	      addr += LINUX_PT_F6_OFF + 16 * (reg - (UNW_IA64_FR + 6));
    156 	      return IA64_LOC_ADDR (addr, IA64_LOC_TYPE_FP);
    157 
    158 	    default:
    159 	      if (unw_is_fpreg (reg))
    160 		return IA64_FPREG_LOC (c, reg);
    161 	      else
    162 		return IA64_REG_LOC (c, reg);
    163 	    }
    164 	}
    165       else if (ia64_get_abi_marker (c) == ABI_MARKER_OLD_LINUX_INTERRUPT)
    166 	{
    167 	  switch (reg)
    168 	    {
    169 	    case UNW_IA64_GR +  1:
    170 	      /* The saved r1 value is valid only in the frame in which
    171 		 it was saved; for everything else we need to look up
    172 		 the appropriate gp value.  */
    173 	      if (c->sigcontext_addr != c->sp + 0x10)
    174 		return IA64_NULL_LOC;
    175 	      addr += LINUX_OLD_PT_R1_OFF;
    176 	      break;
    177 
    178 	    case UNW_IA64_GR +  2 ... UNW_IA64_GR + 3:
    179 	      addr += LINUX_OLD_PT_R2_OFF + 8 * (reg - (UNW_IA64_GR + 2));
    180 	      break;
    181 
    182 	    case UNW_IA64_GR +  8 ... UNW_IA64_GR + 11:
    183 	      addr += LINUX_OLD_PT_R8_OFF + 8 * (reg - (UNW_IA64_GR + 8));
    184 	      break;
    185 
    186 	    case UNW_IA64_GR + 16 ... UNW_IA64_GR + 31:
    187 	      addr += LINUX_OLD_PT_R16_OFF + 8 * (reg - (UNW_IA64_GR + 16));
    188 	      break;
    189 
    190 	    case UNW_IA64_FR + 6 ... UNW_IA64_FR + 9:
    191 	      addr += LINUX_OLD_PT_F6_OFF + 16 * (reg - (UNW_IA64_FR + 6));
    192 	      return IA64_LOC_ADDR (addr, IA64_LOC_TYPE_FP);
    193 
    194 	    case UNW_IA64_BR + 0: addr += LINUX_OLD_PT_B0_OFF; break;
    195 	    case UNW_IA64_BR + 6: addr += LINUX_OLD_PT_B6_OFF; break;
    196 	    case UNW_IA64_BR + 7: addr += LINUX_OLD_PT_B7_OFF; break;
    197 
    198 	    case UNW_IA64_AR_RSC: addr += LINUX_OLD_PT_RSC_OFF; break;
    199 	    case UNW_IA64_AR_CCV: addr += LINUX_OLD_PT_CCV_OFF; break;
    200 
    201 	    default:
    202 	      if (unw_is_fpreg (reg))
    203 		return IA64_FPREG_LOC (c, reg);
    204 	      else
    205 		return IA64_REG_LOC (c, reg);
    206 	    }
    207 	}
    208       if (is_nat)
    209 	{
    210 	  /* For Linux pt-regs structure, bit number is determined by
    211 	     the UNaT slot number (as determined by st8.spill) and the
    212 	     bits are saved wherever the (primary) UNaT was saved.  */
    213 	  *nat_bitnr = ia64_unat_slot_num (addr);
    214 	  return c->loc[IA64_REG_PRI_UNAT_MEM];
    215 	}
    216       return IA64_LOC_ADDR (addr, 0);
    217     }
    218 #endif
    219   return IA64_NULL_LOC;
    220 }
    221 
    222 static inline ia64_loc_t
    223 hpux_scratch_loc (struct cursor *c, unw_regnum_t reg, uint8_t *nat_bitnr)
    224 {
    225 #if !defined(UNW_LOCAL_ONLY) || defined(__hpux)
    226   return IA64_LOC_UC_REG (reg, c->sigcontext_addr);
    227 #else
    228   return IA64_NULL_LOC;
    229 #endif
    230 }
    231 
    232 HIDDEN ia64_loc_t
    233 ia64_scratch_loc (struct cursor *c, unw_regnum_t reg, uint8_t *nat_bitnr)
    234 {
    235   if (c->sigcontext_addr)
    236     {
    237       if (ia64_get_abi (c) == ABI_LINUX)
    238 	return linux_scratch_loc (c, reg, nat_bitnr);
    239       else if (ia64_get_abi (c) ==  ABI_HPUX)
    240 	return hpux_scratch_loc (c, reg, nat_bitnr);
    241       else
    242 	return IA64_NULL_LOC;
    243     }
    244   else
    245     return IA64_REG_LOC (c, reg);
    246 }
    247 
    248 static inline int
    249 update_nat (struct cursor *c, ia64_loc_t nat_loc, unw_word_t mask,
    250 	    unw_word_t *valp, int write)
    251 {
    252   unw_word_t nat_word;
    253   int ret;
    254 
    255   ret = ia64_get (c, nat_loc, &nat_word);
    256   if (ret < 0)
    257     return ret;
    258 
    259   if (write)
    260     {
    261       if (*valp)
    262 	nat_word |= mask;
    263       else
    264 	nat_word &= ~mask;
    265       ret = ia64_put (c, nat_loc, nat_word);
    266     }
    267   else
    268     *valp = (nat_word & mask) != 0;
    269   return ret;
    270 }
    271 
    272 static int
    273 access_nat (struct cursor *c,
    274 	    ia64_loc_t nat_loc, ia64_loc_t reg_loc, uint8_t nat_bitnr,
    275 	    unw_word_t *valp, int write)
    276 {
    277   unw_word_t mask = 0;
    278   unw_fpreg_t tmp;
    279   int ret;
    280 
    281   if (IA64_IS_FP_LOC (reg_loc))
    282     {
    283       /* NaT bit is saved as a NaTVal.  This happens when a general
    284 	 register is saved to a floating-point register.  */
    285       if (write)
    286 	{
    287 	  if (*valp)
    288 	    {
    289 	      if (ia64_is_big_endian (c))
    290 		ret = ia64_putfp (c, reg_loc, unw.nat_val_be);
    291 	      else
    292 		ret = ia64_putfp (c, reg_loc, unw.nat_val_le);
    293 	    }
    294 	  else
    295 	    {
    296 	      unw_word_t *src, *dst;
    297 	      unw_fpreg_t tmp;
    298 
    299 	      ret = ia64_getfp (c, reg_loc, &tmp);
    300 	      if (ret < 0)
    301 		return ret;
    302 
    303 	      /* Reset the exponent to 0x1003e so that the significand
    304 		 will be interpreted as an integer value.  */
    305 	      src = (unw_word_t *) &unw.int_val_be;
    306 	      dst = (unw_word_t *) &tmp;
    307 	      if (!ia64_is_big_endian (c))
    308 		++src, ++dst;
    309 	      *dst = *src;
    310 
    311 	      ret = ia64_putfp (c, reg_loc, tmp);
    312 	    }
    313 	}
    314       else
    315 	{
    316 	  ret = ia64_getfp (c, reg_loc, &tmp);
    317 	  if (ret < 0)
    318 	    return ret;
    319 
    320 	  if (ia64_is_big_endian (c))
    321 	    *valp = (memcmp (&tmp, &unw.nat_val_be, sizeof (tmp)) == 0);
    322 	  else
    323 	    *valp = (memcmp (&tmp, &unw.nat_val_le, sizeof (tmp)) == 0);
    324 	}
    325       return ret;
    326     }
    327 
    328   if ((IA64_IS_REG_LOC (nat_loc)
    329        && (unsigned) (IA64_GET_REG (nat_loc) - UNW_IA64_NAT) < 128)
    330       || IA64_IS_UC_LOC (reg_loc))
    331     {
    332       if (write)
    333 	return ia64_put (c, nat_loc, *valp);
    334       else
    335 	return ia64_get (c, nat_loc, valp);
    336     }
    337 
    338   if (IA64_IS_NULL_LOC (nat_loc))
    339     {
    340       /* NaT bit is not saved. This happens if a general register is
    341 	 saved to a branch register.  Since the NaT bit gets lost, we
    342 	 need to drop it here, too.  Note that if the NaT bit had been
    343 	 set when the save occurred, it would have caused a NaT
    344 	 consumption fault.  */
    345       if (write)
    346 	{
    347 	  if (*valp)
    348 	    return -UNW_EBADREG;	/* can't set NaT bit */
    349 	}
    350       else
    351 	*valp = 0;
    352       return 0;
    353     }
    354 
    355   mask = (unw_word_t) 1 << nat_bitnr;
    356   return update_nat (c, nat_loc, mask, valp, write);
    357 }
    358 
    359 HIDDEN int
    360 tdep_access_reg (struct cursor *c, unw_regnum_t reg, unw_word_t *valp,
    361 		 int write)
    362 {
    363   ia64_loc_t loc, reg_loc, nat_loc;
    364   unw_word_t mask, val;
    365   uint8_t nat_bitnr;
    366   int ret;
    367 
    368   switch (reg)
    369     {
    370       /* frame registers: */
    371 
    372     case UNW_IA64_BSP:
    373       if (write)
    374 	c->bsp = *valp;
    375       else
    376 	*valp = c->bsp;
    377       return 0;
    378 
    379     case UNW_REG_SP:
    380       if (write)
    381 	c->sp = *valp;
    382       else
    383 	*valp = c->sp;
    384       return 0;
    385 
    386     case UNW_REG_IP:
    387       if (write)
    388 	{
    389 	  c->ip = *valp;	/* also update the IP cache */
    390 	  if (c->pi_valid && (*valp < c->pi.start_ip || *valp >= c->pi.end_ip))
    391 	    c->pi_valid = 0;	/* new IP outside of current proc */
    392 	}
    393       loc = c->loc[IA64_REG_IP];
    394       break;
    395 
    396       /* preserved registers: */
    397 
    398     case UNW_IA64_GR + 4 ... UNW_IA64_GR + 7:
    399       loc = c->loc[IA64_REG_R4 + (reg - (UNW_IA64_GR + 4))];
    400       break;
    401 
    402     case UNW_IA64_NAT + 4 ... UNW_IA64_NAT + 7:
    403       loc = c->loc[IA64_REG_NAT4 + (reg - (UNW_IA64_NAT + 4))];
    404       reg_loc = c->loc[IA64_REG_R4 + (reg - (UNW_IA64_NAT + 4))];
    405       nat_bitnr = c->nat_bitnr[reg - (UNW_IA64_NAT + 4)];
    406       return access_nat (c, loc, reg_loc, nat_bitnr, valp, write);
    407 
    408     case UNW_IA64_AR_BSP:	loc = c->loc[IA64_REG_BSP]; break;
    409     case UNW_IA64_AR_BSPSTORE:	loc = c->loc[IA64_REG_BSPSTORE]; break;
    410     case UNW_IA64_AR_PFS:	loc = c->loc[IA64_REG_PFS]; break;
    411     case UNW_IA64_AR_RNAT:	loc = c->loc[IA64_REG_RNAT]; break;
    412     case UNW_IA64_AR_UNAT:	loc = c->loc[IA64_REG_UNAT]; break;
    413     case UNW_IA64_AR_LC:	loc = c->loc[IA64_REG_LC]; break;
    414     case UNW_IA64_AR_FPSR:	loc = c->loc[IA64_REG_FPSR]; break;
    415     case UNW_IA64_BR + 1:	loc = c->loc[IA64_REG_B1]; break;
    416     case UNW_IA64_BR + 2:	loc = c->loc[IA64_REG_B2]; break;
    417     case UNW_IA64_BR + 3:	loc = c->loc[IA64_REG_B3]; break;
    418     case UNW_IA64_BR + 4:	loc = c->loc[IA64_REG_B4]; break;
    419     case UNW_IA64_BR + 5:	loc = c->loc[IA64_REG_B5]; break;
    420 
    421     case UNW_IA64_CFM:
    422       if (write)
    423 	c->cfm = *valp;	/* also update the CFM cache */
    424       loc = c->cfm_loc;
    425       break;
    426 
    427     case UNW_IA64_PR:
    428       /*
    429        * Note: broad-side access to the predicates is NOT rotated
    430        * (i.e., it is done as if CFM.rrb.pr == 0.
    431        */
    432       if (write)
    433 	{
    434 	  c->pr = *valp;		/* update the predicate cache */
    435 	  return ia64_put (c, c->loc[IA64_REG_PR], *valp);
    436 	}
    437       else
    438 	return ia64_get (c, c->loc[IA64_REG_PR], valp);
    439 
    440     case UNW_IA64_GR + 32 ... UNW_IA64_GR + 127:	/* stacked reg */
    441       reg = rotate_gr (c, reg - UNW_IA64_GR);
    442       if (reg < 0)
    443 	return -UNW_EBADREG;
    444       ret = ia64_get_stacked (c, reg, &loc, NULL);
    445       if (ret < 0)
    446 	return ret;
    447       break;
    448 
    449     case UNW_IA64_NAT + 32 ... UNW_IA64_NAT + 127:	/* stacked reg */
    450       reg = rotate_gr (c, reg - UNW_IA64_NAT);
    451       if (reg < 0)
    452 	return -UNW_EBADREG;
    453       ret = ia64_get_stacked (c, reg, &loc, &nat_loc);
    454       if (ret < 0)
    455 	return ret;
    456       assert (!IA64_IS_REG_LOC (loc));
    457       mask = (unw_word_t) 1 << rse_slot_num (IA64_GET_ADDR (loc));
    458       return update_nat (c, nat_loc, mask, valp, write);
    459 
    460     case UNW_IA64_AR_EC:
    461       if ((ret = ia64_get (c, c->ec_loc, &val)) < 0)
    462 	return ret;
    463 
    464       if (write)
    465 	{
    466 	  val = ((val & ~((unw_word_t) 0x3f << 52)) | ((*valp & 0x3f) << 52));
    467 	  return ia64_put (c, c->ec_loc, val);
    468 	}
    469       else
    470 	{
    471 	  *valp = (val >> 52) & 0x3f;
    472 	  return 0;
    473 	}
    474 
    475       /* scratch & special registers: */
    476 
    477     case UNW_IA64_GR + 0:
    478       if (write)
    479 	return -UNW_EREADONLYREG;
    480       *valp = 0;
    481       return 0;
    482 
    483     case UNW_IA64_NAT + 0:
    484       if (write)
    485 	return -UNW_EREADONLYREG;
    486       *valp = 0;
    487       return 0;
    488 
    489     case UNW_IA64_NAT + 1:
    490     case UNW_IA64_NAT + 2 ... UNW_IA64_NAT + 3:
    491     case UNW_IA64_NAT + 8 ... UNW_IA64_NAT + 31:
    492       loc = ia64_scratch_loc (c, reg, &nat_bitnr);
    493       if (IA64_IS_NULL_LOC (loc) && reg == UNW_IA64_NAT + 1)
    494 	{
    495 	  /* access to GP */
    496 	  if (write)
    497 	    return -UNW_EREADONLYREG;
    498 	  *valp = 0;
    499 	  return 0;
    500 	}
    501       if (!(IA64_IS_REG_LOC (loc) || IA64_IS_UC_LOC (loc)
    502 	    || IA64_IS_FP_LOC (loc)))
    503 	/* We're dealing with a NaT bit stored in memory.  */
    504 	return update_nat(c, loc, (unw_word_t) 1 << nat_bitnr, valp, write);
    505       break;
    506 
    507     case UNW_IA64_GR + 15 ... UNW_IA64_GR + 18:
    508       mask = 1 << (reg - (UNW_IA64_GR + 15));
    509       if (write)
    510 	{
    511 	  c->eh_args[reg - (UNW_IA64_GR + 15)] = *valp;
    512 	  c->eh_valid_mask |= mask;
    513 	  return 0;
    514 	}
    515       else if ((c->eh_valid_mask & mask) != 0)
    516 	{
    517 	  *valp = c->eh_args[reg - (UNW_IA64_GR + 15)];
    518 	  return 0;
    519 	}
    520       else
    521 	loc = ia64_scratch_loc (c, reg, NULL);
    522       break;
    523 
    524     case UNW_IA64_GR +  1:				/* global pointer */
    525     case UNW_IA64_GR +  2 ... UNW_IA64_GR + 3:
    526     case UNW_IA64_GR +  8 ... UNW_IA64_GR + 14:
    527     case UNW_IA64_GR + 19 ... UNW_IA64_GR + 31:
    528     case UNW_IA64_BR + 0:
    529     case UNW_IA64_BR + 6:
    530     case UNW_IA64_BR + 7:
    531     case UNW_IA64_AR_RSC:
    532     case UNW_IA64_AR_CSD:
    533     case UNW_IA64_AR_SSD:
    534     case UNW_IA64_AR_CCV:
    535       loc = ia64_scratch_loc (c, reg, NULL);
    536       if (IA64_IS_NULL_LOC (loc) && reg == UNW_IA64_GR + 1)
    537 	{
    538 	  /* access to GP */
    539 	  if (write)
    540 	    return -UNW_EREADONLYREG;
    541 
    542 	  /* ensure c->pi is up-to-date: */
    543 	  if ((ret = ia64_make_proc_info (c)) < 0)
    544 	    return ret;
    545 	  *valp = c->pi.gp;
    546 	  return 0;
    547 	}
    548       break;
    549 
    550     default:
    551       Debug (1, "bad register number %d\n", reg);
    552       return -UNW_EBADREG;
    553     }
    554 
    555   if (write)
    556     return ia64_put (c, loc, *valp);
    557   else
    558     return ia64_get (c, loc, valp);
    559 }
    560 
    561 HIDDEN int
    562 tdep_access_fpreg (struct cursor *c, int reg, unw_fpreg_t *valp,
    563 		   int write)
    564 {
    565   ia64_loc_t loc;
    566 
    567   switch (reg)
    568     {
    569     case UNW_IA64_FR + 0:
    570       if (write)
    571 	return -UNW_EREADONLYREG;
    572       *valp = unw.read_only.f0;
    573       return 0;
    574 
    575     case UNW_IA64_FR + 1:
    576       if (write)
    577 	return -UNW_EREADONLYREG;
    578 
    579       if (ia64_is_big_endian (c))
    580 	*valp = unw.read_only.f1_be;
    581       else
    582 	*valp = unw.read_only.f1_le;
    583       return 0;
    584 
    585     case UNW_IA64_FR + 2: loc = c->loc[IA64_REG_F2]; break;
    586     case UNW_IA64_FR + 3: loc = c->loc[IA64_REG_F3]; break;
    587     case UNW_IA64_FR + 4: loc = c->loc[IA64_REG_F4]; break;
    588     case UNW_IA64_FR + 5: loc = c->loc[IA64_REG_F5]; break;
    589 
    590     case UNW_IA64_FR + 16 ... UNW_IA64_FR + 31:
    591       loc = c->loc[IA64_REG_F16 + (reg - (UNW_IA64_FR + 16))];
    592       break;
    593 
    594     case UNW_IA64_FR + 6 ... UNW_IA64_FR + 15:
    595       loc = ia64_scratch_loc (c, reg, NULL);
    596       break;
    597 
    598     case UNW_IA64_FR + 32 ... UNW_IA64_FR + 127:
    599       reg = rotate_fr (c, reg - UNW_IA64_FR) + UNW_IA64_FR;
    600       loc = ia64_scratch_loc (c, reg, NULL);
    601       break;
    602 
    603     default:
    604       Debug (1, "bad register number %d\n", reg);
    605       return -UNW_EBADREG;
    606     }
    607 
    608   if (write)
    609     return ia64_putfp (c, loc, *valp);
    610   else
    611     return ia64_getfp (c, loc, valp);
    612 }
    613