1 /* libunwind - a platform-independent unwind library
2    Copyright (C) 2001-2005 Hewlett-Packard Co
3 	Contributed by David Mosberger-Tang <davidm@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
linux_scratch_loc(struct cursor * c,unw_regnum_t reg,uint8_t * nat_bitnr)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
hpux_scratch_loc(struct cursor * c,unw_regnum_t reg,uint8_t * nat_bitnr)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
ia64_scratch_loc(struct cursor * c,unw_regnum_t reg,uint8_t * nat_bitnr)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
update_nat(struct cursor * c,ia64_loc_t nat_loc,unw_word_t mask,unw_word_t * valp,int write)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
access_nat(struct cursor * c,ia64_loc_t nat_loc,ia64_loc_t reg_loc,uint8_t nat_bitnr,unw_word_t * valp,int write)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
tdep_access_reg(struct cursor * c,unw_regnum_t reg,unw_word_t * valp,int write)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
tdep_access_fpreg(struct cursor * c,int reg,unw_fpreg_t * valp,int write)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