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