1 /* libunwind - a platform-independent unwind library
2 Copyright (C) 2001-2005 Hewlett-Packard Co
3 Copyright (C) 2007 David Mosberger-Tang
4 Contributed by David Mosberger-Tang <dmosberger@gmail.com>
6 This file is part of libunwind.
8 Permission is hereby granted, free of charge, to any person obtaining
9 a copy of this software and associated documentation files (the
10 "Software"), to deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify, merge, publish,
12 distribute, sublicense, and/or sell copies of the Software, and to
13 permit persons to whom the Software is furnished to do so, subject to
14 the following conditions:
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
19 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29 #ifdef HAVE_SYS_UC_ACCESS_H
30 # include <sys/uc_access.h>
33 #ifdef UNW_REMOTE_ONLY
35 /* unw_local_addr_space is a NULL pointer in this case. */
36 PROTECTED unw_addr_space_t unw_local_addr_space;
38 #else /* !UNW_REMOTE_ONLY */
40 static struct unw_addr_space local_addr_space;
42 PROTECTED unw_addr_space_t unw_local_addr_space = &local_addr_space;
44 #ifdef HAVE_SYS_UC_ACCESS_H
46 #else /* !HAVE_SYS_UC_ACCESS_H */
49 tdep_uc_addr (ucontext_t *uc, int reg, uint8_t *nat_bitnr)
51 return inlined_uc_addr (uc, reg, nat_bitnr);
54 #endif /* !HAVE_SYS_UC_ACCESS_H */
57 put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
63 get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
66 #ifndef UNW_LOCAL_ONLY
67 # pragma weak _U_dyn_info_list_addr
68 if (!_U_dyn_info_list_addr)
71 *dyn_info_list_addr = _U_dyn_info_list_addr ();
76 access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
81 Debug (12, "mem[%lx] <- %lx\n", addr, *val);
82 *(unw_word_t *) addr = *val;
86 *val = *(unw_word_t *) addr;
87 Debug (12, "mem[%lx] -> %lx\n", addr, *val);
92 #ifdef HAVE_SYS_UC_ACCESS_H
94 #define SYSCALL_CFM_SAVE_REG 11 /* on a syscall, ar.pfs is saved in r11 */
95 #define REASON_SYSCALL 0
98 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
101 ucontext_t *uc = arg;
102 unsigned int nat, mask;
107 __uc_get_reason (uc, &reason);
111 case UNW_IA64_GR ... UNW_IA64_GR + 31:
112 if ((ret = __uc_get_grs (uc, (reg - UNW_IA64_GR), 1, &value, &nat)))
116 ret = __uc_set_grs (uc, (reg - UNW_IA64_GR), 1, val, nat);
121 case UNW_IA64_NAT ... UNW_IA64_NAT + 31:
122 if ((ret = __uc_get_grs (uc, (reg - UNW_IA64_GR), 1, &value, &nat)))
125 mask = 1 << (reg - UNW_IA64_GR);
133 ret = __uc_set_grs (uc, (reg - UNW_IA64_GR), 1, &value, nat);
136 *val = (nat & mask) != 0;
139 case UNW_IA64_AR ... UNW_IA64_AR + 127:
140 if (reg == UNW_IA64_AR_BSP)
143 ret = __uc_set_ar (uc, (reg - UNW_IA64_AR), *val);
145 ret = __uc_get_ar (uc, (reg - UNW_IA64_AR), val);
147 else if (reg == UNW_IA64_AR_PFS && reason == REASON_SYSCALL)
149 /* As of HP-UX 11.22, getcontext() does not have unwind info
150 and because of that, we need to hack thins manually here.
151 Hopefully, this is OK because the HP-UX kernel also needs
152 to know where AR.PFS has been saved, so the use of
153 register r11 for this purpose is pretty much nailed
156 ret = __uc_set_grs (uc, SYSCALL_CFM_SAVE_REG, 1, val, 0);
158 ret = __uc_get_grs (uc, SYSCALL_CFM_SAVE_REG, 1, val, &nat);
163 ret = __uc_set_ar (uc, (reg - UNW_IA64_AR), *val);
165 ret = __uc_get_ar (uc, (reg - UNW_IA64_AR), val);
169 case UNW_IA64_BR ... UNW_IA64_BR + 7:
171 ret = __uc_set_brs (uc, (reg - UNW_IA64_BR), 1, val);
173 ret = __uc_get_brs (uc, (reg - UNW_IA64_BR), 1, val);
178 ret = __uc_set_prs (uc, *val);
180 ret = __uc_get_prs (uc, val);
185 ret = __uc_set_ip (uc, *val);
187 ret = __uc_get_ip (uc, val);
192 ret = __uc_set_cfm (uc, *val);
194 ret = __uc_get_cfm (uc, val);
197 case UNW_IA64_FR ... UNW_IA64_FR + 127:
205 Debug (1, "failed to %s %s (ret = %d)\n",
206 write ? "write" : "read", unw_regname (reg), ret);
211 Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
213 Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
218 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
219 int write, void *arg)
221 ucontext_t *uc = arg;
222 fp_regval_t fp_regval;
227 case UNW_IA64_FR ... UNW_IA64_FR + 127:
230 memcpy (&fp_regval, val, sizeof (fp_regval));
231 ret = __uc_set_frs (uc, (reg - UNW_IA64_FR), 1, &fp_regval);
235 ret = __uc_get_frs (uc, (reg - UNW_IA64_FR), 1, &fp_regval);
236 memcpy (val, &fp_regval, sizeof (*val));
250 #else /* !HAVE_SYS_UC_ACCESS_H */
253 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
256 unw_word_t *addr, mask;
257 ucontext_t *uc = arg;
259 if (reg >= UNW_IA64_NAT + 4 && reg <= UNW_IA64_NAT + 7)
261 mask = ((unw_word_t) 1) << (reg - UNW_IA64_NAT);
265 uc->uc_mcontext.sc_nat |= mask;
267 uc->uc_mcontext.sc_nat &= ~mask;
270 *val = (uc->uc_mcontext.sc_nat & mask) != 0;
273 Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
275 Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
279 addr = tdep_uc_addr (uc, reg, NULL);
285 if (ia64_read_only_reg (addr))
287 Debug (16, "attempt to write read-only register\n");
288 return -UNW_EREADONLYREG;
291 Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
295 *val = *(unw_word_t *) addr;
296 Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
301 Debug (1, "bad register number %u\n", reg);
306 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
307 int write, void *arg)
309 ucontext_t *uc = arg;
312 if (reg < UNW_IA64_FR || reg >= UNW_IA64_FR + 128)
315 addr = tdep_uc_addr (uc, reg, NULL);
321 if (ia64_read_only_reg (addr))
323 Debug (16, "attempt to write read-only register\n");
324 return -UNW_EREADONLYREG;
327 Debug (12, "%s <- %016lx.%016lx\n",
328 unw_regname (reg), val->raw.bits[1], val->raw.bits[0]);
332 *val = *(unw_fpreg_t *) addr;
333 Debug (12, "%s -> %016lx.%016lx\n",
334 unw_regname (reg), val->raw.bits[1], val->raw.bits[0]);
339 Debug (1, "bad register number %u\n", reg);
340 /* attempt to access a non-preserved register */
344 #endif /* !HAVE_SYS_UC_ACCESS_H */
347 get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
348 char *buf, size_t buf_len, unw_word_t *offp,
351 return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
355 ia64_local_addr_space_init (void)
357 memset (&local_addr_space, 0, sizeof (local_addr_space));
358 local_addr_space.big_endian = (__BYTE_ORDER == __BIG_ENDIAN);
360 local_addr_space.abi = ABI_LINUX;
361 #elif defined(__hpux)
362 local_addr_space.abi = ABI_HPUX;
364 local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
365 local_addr_space.acc.find_proc_info = tdep_find_proc_info;
366 local_addr_space.acc.put_unwind_info = put_unwind_info;
367 local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
368 local_addr_space.acc.access_mem = access_mem;
369 local_addr_space.acc.access_reg = access_reg;
370 local_addr_space.acc.access_fpreg = access_fpreg;
371 local_addr_space.acc.resume = ia64_local_resume;
372 local_addr_space.acc.get_proc_name = get_static_proc_name;
373 unw_flush_cache (&local_addr_space, 0, 0);
376 #endif /* !UNW_REMOTE_ONLY */
378 #ifndef UNW_LOCAL_ONLY
381 ia64_uc_access_reg (struct cursor *c, ia64_loc_t loc, unw_word_t *valp,
384 #ifdef HAVE_SYS_UC_ACCESS_H
385 unw_word_t uc_addr = IA64_GET_AUX_ADDR (loc);
389 Debug (16, "%s location %s\n",
390 write ? "writing" : "reading", ia64_strloc (loc));
392 if (c->as == unw_local_addr_space)
393 ucp = (ucontext_t *) uc_addr;
396 unw_word_t *dst, src;
398 /* Need to copy-in ucontext_t first. */
399 ucp = alloca (sizeof (ucontext_t));
403 /* For now, there is no non-HP-UX implementation of the
404 uc_access(3) interface. Because of that, we cannot, e.g.,
405 unwind an HP-UX program from a Linux program. Should that
406 become possible at some point in the future, the
407 copy-in/copy-out needs to be adjusted to do byte-swapping if
409 assert (c->as->big_endian == (__BYTE_ORDER == __BIG_ENDIAN));
411 dst = (unw_word_t *) ucp;
412 for (src = uc_addr; src < uc_addr + sizeof (ucontext_t); src += 8)
413 if ((ret = (*c->as->acc.access_mem) (c->as, src, dst++, 0, c->as_arg))
418 if (IA64_IS_REG_LOC (loc))
419 ret = access_reg (unw_local_addr_space, IA64_GET_REG (loc), valp, write,
423 /* Must be an access to the RSE backing store in ucontext_t. */
424 unw_word_t addr = IA64_GET_ADDR (loc);
427 ret = __uc_set_rsebs (ucp, (uint64_t *) addr, 1, valp);
429 ret = __uc_get_rsebs (ucp, (uint64_t *) addr, 1, valp);
436 if (write && c->as != unw_local_addr_space)
438 /* need to copy-out ucontext_t: */
439 unw_word_t dst, *src = (unw_word_t *) ucp;
440 for (dst = uc_addr; dst < uc_addr + sizeof (ucontext_t); dst += 8)
441 if ((ret = (*c->as->acc.access_mem) (c->as, dst, src++, 1, c->as_arg))
446 #else /* !HAVE_SYS_UC_ACCESS_H */
448 #endif /* !HAVE_SYS_UC_ACCESS_H */
452 ia64_uc_access_fpreg (struct cursor *c, ia64_loc_t loc, unw_fpreg_t *valp,
455 #ifdef HAVE_SYS_UC_ACCESS_H
456 unw_word_t uc_addr = IA64_GET_AUX_ADDR (loc);
460 if (c->as == unw_local_addr_space)
461 ucp = (ucontext_t *) uc_addr;
464 unw_word_t *dst, src;
466 /* Need to copy-in ucontext_t first. */
467 ucp = alloca (sizeof (ucontext_t));
471 /* For now, there is no non-HP-UX implementation of the
472 uc_access(3) interface. Because of that, we cannot, e.g.,
473 unwind an HP-UX program from a Linux program. Should that
474 become possible at some point in the future, the
475 copy-in/copy-out needs to be adjusted to do byte-swapping if
477 assert (c->as->big_endian == (__BYTE_ORDER == __BIG_ENDIAN));
479 dst = (unw_word_t *) ucp;
480 for (src = uc_addr; src < uc_addr + sizeof (ucontext_t); src += 8)
481 if ((ret = (*c->as->acc.access_mem) (c->as, src, dst++, 0, c->as_arg))
486 if ((ret = access_fpreg (unw_local_addr_space, IA64_GET_REG (loc), valp,
490 if (write && c->as != unw_local_addr_space)
492 /* need to copy-out ucontext_t: */
493 unw_word_t dst, *src = (unw_word_t *) ucp;
494 for (dst = uc_addr; dst < uc_addr + sizeof (ucontext_t); dst += 8)
495 if ((ret = (*c->as->acc.access_mem) (c->as, dst, src++, 1, c->as_arg))
500 #else /* !HAVE_SYS_UC_ACCESS_H */
502 #endif /* !HAVE_SYS_UC_ACCESS_H */
505 #endif /* UNW_LOCAL_ONLY */