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>
5
6This file is part of libunwind.
7
8Permission is hereby granted, free of charge, to any person obtaining
9a copy of this software and associated documentation files (the
10"Software"), to deal in the Software without restriction, including
11without limitation the rights to use, copy, modify, merge, publish,
12distribute, sublicense, and/or sell copies of the Software, and to
13permit persons to whom the Software is furnished to do so, subject to
14the following conditions:
15
16The above copyright notice and this permission notice shall be
17included in all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
26
27#include "unwind_i.h"
28
29#ifdef HAVE_SYS_UC_ACCESS_H
30# include <sys/uc_access.h>
31#endif
32
33#ifdef UNW_REMOTE_ONLY
34
35/* unw_local_addr_space is a NULL pointer in this case.  */
36PROTECTED unw_addr_space_t unw_local_addr_space;
37
38#else /* !UNW_REMOTE_ONLY */
39
40static struct unw_addr_space local_addr_space;
41
42PROTECTED unw_addr_space_t unw_local_addr_space = &local_addr_space;
43
44#ifdef HAVE_SYS_UC_ACCESS_H
45
46#else /* !HAVE_SYS_UC_ACCESS_H */
47
48HIDDEN void *
49tdep_uc_addr (ucontext_t *uc, int reg, uint8_t *nat_bitnr)
50{
51  return inlined_uc_addr (uc, reg, nat_bitnr);
52}
53
54#endif /* !HAVE_SYS_UC_ACCESS_H */
55
56static void
57put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
58{
59  /* it's a no-op */
60}
61
62static int
63get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
64			void *arg)
65{
66#ifndef UNW_LOCAL_ONLY
67# pragma weak _U_dyn_info_list_addr
68  if (!_U_dyn_info_list_addr)
69    return -UNW_ENOINFO;
70#endif
71  *dyn_info_list_addr = _U_dyn_info_list_addr ();
72  return 0;
73}
74
75static int
76access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
77	    void *arg)
78{
79  if (write)
80    {
81      /* ANDROID support update. */
82#ifdef UNW_LOCAL_ONLY
83      if (map_local_is_writable (addr))
84        {
85#endif
86          Debug (12, "mem[%lx] <- %lx\n", addr, *val);
87          *(unw_word_t *) addr = *val;
88#ifdef UNW_LOCAL_ONLY
89        }
90      else
91        {
92          Debug (12, "Unwritable memory mem[%lx] <- %lx\n", addr, *val);
93          return -1;
94        }
95#endif
96      /* End of ANDROID update. */
97    }
98  else
99    {
100      /* ANDROID support update. */
101#ifdef UNW_LOCAL_ONLY
102      if (map_local_is_readable (addr))
103        {
104#endif
105          *val = *(unw_word_t *) addr;
106          Debug (12, "mem[%lx] -> %lx\n", addr, *val);
107#ifdef UNW_LOCAL_ONLY
108        }
109      else
110        {
111          Debug (12, "Unreadable memory mem[%lx] -> XXX\n", addr);
112          return -1;
113        }
114#endif
115      /* End of ANDROID update. */
116    }
117  return 0;
118}
119
120#ifdef HAVE_SYS_UC_ACCESS_H
121
122#define SYSCALL_CFM_SAVE_REG	11 /* on a syscall, ar.pfs is saved in r11 */
123#define REASON_SYSCALL		0
124
125static int
126access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
127	    void *arg)
128{
129  ucontext_t *uc = arg;
130  unsigned int nat, mask;
131  uint64_t value;
132  uint16_t reason;
133  int ret;
134
135  __uc_get_reason (uc, &reason);
136
137  switch (reg)
138    {
139    case UNW_IA64_GR  ... UNW_IA64_GR + 31:
140      if ((ret = __uc_get_grs (uc, (reg - UNW_IA64_GR), 1, &value, &nat)))
141	break;
142
143      if (write)
144	ret = __uc_set_grs (uc, (reg - UNW_IA64_GR), 1, val, nat);
145      else
146	*val = value;
147      break;
148
149    case UNW_IA64_NAT ... UNW_IA64_NAT + 31:
150      if ((ret = __uc_get_grs (uc, (reg - UNW_IA64_GR), 1, &value, &nat)))
151	break;
152
153      mask = 1 << (reg - UNW_IA64_GR);
154
155      if (write)
156	{
157	  if (*val)
158	    nat |= mask;
159	  else
160	    nat &= ~mask;
161	  ret = __uc_set_grs (uc, (reg - UNW_IA64_GR), 1, &value, nat);
162	}
163      else
164	*val = (nat & mask) != 0;
165      break;
166
167    case UNW_IA64_AR  ... UNW_IA64_AR + 127:
168      if (reg == UNW_IA64_AR_BSP)
169	{
170  	  if (write)
171	    ret = __uc_set_ar (uc, (reg - UNW_IA64_AR), *val);
172 	  else
173 	    ret = __uc_get_ar (uc, (reg - UNW_IA64_AR), val);
174	}
175      else if (reg == UNW_IA64_AR_PFS && reason == REASON_SYSCALL)
176 	{
177	  /* As of HP-UX 11.22, getcontext() does not have unwind info
178	     and because of that, we need to hack thins manually here.
179	     Hopefully, this is OK because the HP-UX kernel also needs
180	     to know where AR.PFS has been saved, so the use of
181	     register r11 for this purpose is pretty much nailed
182	     down.  */
183 	  if (write)
184 	    ret = __uc_set_grs (uc, SYSCALL_CFM_SAVE_REG, 1, val, 0);
185 	  else
186 	    ret = __uc_get_grs (uc, SYSCALL_CFM_SAVE_REG, 1, val, &nat);
187 	}
188      else
189	{
190	  if (write)
191	    ret = __uc_set_ar (uc, (reg - UNW_IA64_AR), *val);
192	  else
193	    ret = __uc_get_ar (uc, (reg - UNW_IA64_AR), val);
194	}
195      break;
196
197    case UNW_IA64_BR  ... UNW_IA64_BR + 7:
198      if (write)
199	ret = __uc_set_brs (uc, (reg - UNW_IA64_BR), 1, val);
200      else
201	ret = __uc_get_brs (uc, (reg - UNW_IA64_BR), 1, val);
202      break;
203
204    case UNW_IA64_PR:
205      if (write)
206	ret = __uc_set_prs (uc, *val);
207      else
208	ret = __uc_get_prs (uc, val);
209      break;
210
211    case UNW_IA64_IP:
212      if (write)
213	ret = __uc_set_ip (uc, *val);
214      else
215	ret = __uc_get_ip (uc, val);
216      break;
217
218    case UNW_IA64_CFM:
219      if (write)
220	ret = __uc_set_cfm (uc, *val);
221      else
222	ret = __uc_get_cfm (uc, val);
223      break;
224
225    case UNW_IA64_FR  ... UNW_IA64_FR + 127:
226    default:
227      ret = EINVAL;
228      break;
229    }
230
231  if (ret != 0)
232    {
233      Debug (1, "failed to %s %s (ret = %d)\n",
234	     write ? "write" : "read", unw_regname (reg), ret);
235      return -UNW_EBADREG;
236    }
237
238  if (write)
239    Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
240  else
241    Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
242  return 0;
243}
244
245static int
246access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
247	      int write, void *arg)
248{
249  ucontext_t *uc = arg;
250  fp_regval_t fp_regval;
251  int ret;
252
253  switch (reg)
254    {
255    case UNW_IA64_FR  ... UNW_IA64_FR + 127:
256      if (write)
257	{
258	  memcpy (&fp_regval, val, sizeof (fp_regval));
259	  ret = __uc_set_frs (uc, (reg - UNW_IA64_FR), 1, &fp_regval);
260	}
261      else
262	{
263	  ret = __uc_get_frs (uc, (reg - UNW_IA64_FR), 1, &fp_regval);
264	  memcpy (val, &fp_regval, sizeof (*val));
265	}
266      break;
267
268    default:
269      ret = EINVAL;
270      break;
271    }
272  if (ret != 0)
273    return -UNW_EBADREG;
274
275  return 0;
276}
277
278#else /* !HAVE_SYS_UC_ACCESS_H */
279
280static int
281access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
282	    void *arg)
283{
284  unw_word_t *addr, mask;
285  ucontext_t *uc = arg;
286
287  if (reg >= UNW_IA64_NAT + 4 && reg <= UNW_IA64_NAT + 7)
288    {
289      mask = ((unw_word_t) 1) << (reg - UNW_IA64_NAT);
290      if (write)
291	{
292	  if (*val)
293	    uc->uc_mcontext.sc_nat |= mask;
294	  else
295	    uc->uc_mcontext.sc_nat &= ~mask;
296	}
297      else
298	*val = (uc->uc_mcontext.sc_nat & mask) != 0;
299
300      if (write)
301	Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
302      else
303	Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
304      return 0;
305    }
306
307  addr = tdep_uc_addr (uc, reg, NULL);
308  if (!addr)
309    goto badreg;
310
311  if (write)
312    {
313      if (ia64_read_only_reg (addr))
314	{
315	  Debug (16, "attempt to write read-only register\n");
316	  return -UNW_EREADONLYREG;
317	}
318      *addr = *val;
319      Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
320    }
321  else
322    {
323      *val = *(unw_word_t *) addr;
324      Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
325    }
326  return 0;
327
328 badreg:
329  Debug (1, "bad register number %u\n", reg);
330  return -UNW_EBADREG;
331}
332
333static int
334access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
335	      int write, void *arg)
336{
337  ucontext_t *uc = arg;
338  unw_fpreg_t *addr;
339
340  if (reg < UNW_IA64_FR || reg >= UNW_IA64_FR + 128)
341    goto badreg;
342
343  addr = tdep_uc_addr (uc, reg, NULL);
344  if (!addr)
345    goto badreg;
346
347  if (write)
348    {
349      if (ia64_read_only_reg (addr))
350	{
351	  Debug (16, "attempt to write read-only register\n");
352	  return -UNW_EREADONLYREG;
353	}
354      *addr = *val;
355      Debug (12, "%s <- %016lx.%016lx\n",
356	     unw_regname (reg), val->raw.bits[1], val->raw.bits[0]);
357    }
358  else
359    {
360      *val = *(unw_fpreg_t *) addr;
361      Debug (12, "%s -> %016lx.%016lx\n",
362	     unw_regname (reg), val->raw.bits[1], val->raw.bits[0]);
363    }
364  return 0;
365
366 badreg:
367  Debug (1, "bad register number %u\n", reg);
368  /* attempt to access a non-preserved register */
369  return -UNW_EBADREG;
370}
371
372#endif /* !HAVE_SYS_UC_ACCESS_H */
373
374static int
375get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
376		      char *buf, size_t buf_len, unw_word_t *offp,
377		      void *arg)
378{
379  return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
380}
381
382HIDDEN void
383ia64_local_addr_space_init (void)
384{
385  memset (&local_addr_space, 0, sizeof (local_addr_space));
386  local_addr_space.big_endian = (__BYTE_ORDER == __BIG_ENDIAN);
387#if defined(__linux)
388  local_addr_space.abi = ABI_LINUX;
389#elif defined(__hpux)
390  local_addr_space.abi = ABI_HPUX;
391#endif
392  local_addr_space.caching_policy = UNW_CACHE_GLOBAL;
393  local_addr_space.acc.find_proc_info = tdep_find_proc_info;
394  local_addr_space.acc.put_unwind_info = put_unwind_info;
395  local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
396  local_addr_space.acc.access_mem = access_mem;
397  local_addr_space.acc.access_reg = access_reg;
398  local_addr_space.acc.access_fpreg = access_fpreg;
399  local_addr_space.acc.resume = ia64_local_resume;
400  local_addr_space.acc.get_proc_name = get_static_proc_name;
401  unw_flush_cache (&local_addr_space, 0, 0);
402
403  map_local_init ();
404}
405
406#endif /* !UNW_REMOTE_ONLY */
407
408#ifndef UNW_LOCAL_ONLY
409
410HIDDEN int
411ia64_uc_access_reg (struct cursor *c, ia64_loc_t loc, unw_word_t *valp,
412		    int write)
413{
414#ifdef HAVE_SYS_UC_ACCESS_H
415  unw_word_t uc_addr = IA64_GET_AUX_ADDR (loc);
416  ucontext_t *ucp;
417  int ret;
418
419  Debug (16, "%s location %s\n",
420	 write ? "writing" : "reading", ia64_strloc (loc));
421
422  if (c->as == unw_local_addr_space)
423    ucp = (ucontext_t *) uc_addr;
424  else
425    {
426      unw_word_t *dst, src;
427
428      /* Need to copy-in ucontext_t first.  */
429      ucp = alloca (sizeof (ucontext_t));
430      if (!ucp)
431	return -UNW_ENOMEM;
432
433      /* For now, there is no non-HP-UX implementation of the
434         uc_access(3) interface.  Because of that, we cannot, e.g.,
435         unwind an HP-UX program from a Linux program.  Should that
436         become possible at some point in the future, the
437         copy-in/copy-out needs to be adjusted to do byte-swapping if
438         necessary. */
439      assert (c->as->big_endian == (__BYTE_ORDER == __BIG_ENDIAN));
440
441      dst = (unw_word_t *) ucp;
442      for (src = uc_addr; src < uc_addr + sizeof (ucontext_t); src += 8)
443	if ((ret = (*c->as->acc.access_mem) (c->as, src, dst++, 0, c->as_arg))
444	    < 0)
445	  return ret;
446    }
447
448  if (IA64_IS_REG_LOC (loc))
449    ret = access_reg (unw_local_addr_space, IA64_GET_REG (loc), valp, write,
450		      ucp);
451  else
452    {
453      /* Must be an access to the RSE backing store in ucontext_t.  */
454      unw_word_t addr = IA64_GET_ADDR (loc);
455
456      if (write)
457	ret = __uc_set_rsebs (ucp, (uint64_t *) addr, 1, valp);
458      else
459	ret = __uc_get_rsebs (ucp, (uint64_t *) addr, 1, valp);
460      if (ret != 0)
461	ret = -UNW_EBADREG;
462    }
463  if (ret < 0)
464    return ret;
465
466  if (write && c->as != unw_local_addr_space)
467    {
468      /* need to copy-out ucontext_t: */
469      unw_word_t dst, *src = (unw_word_t *) ucp;
470      for (dst = uc_addr; dst < uc_addr + sizeof (ucontext_t); dst += 8)
471	if ((ret = (*c->as->acc.access_mem) (c->as, dst, src++, 1, c->as_arg))
472	    < 0)
473	  return ret;
474    }
475  return 0;
476#else /* !HAVE_SYS_UC_ACCESS_H */
477  return -UNW_EINVAL;
478#endif /* !HAVE_SYS_UC_ACCESS_H */
479}
480
481HIDDEN int
482ia64_uc_access_fpreg (struct cursor *c, ia64_loc_t loc, unw_fpreg_t *valp,
483		      int write)
484{
485#ifdef HAVE_SYS_UC_ACCESS_H
486  unw_word_t uc_addr = IA64_GET_AUX_ADDR (loc);
487  ucontext_t *ucp;
488  int ret;
489
490  if (c->as == unw_local_addr_space)
491    ucp = (ucontext_t *) uc_addr;
492  else
493    {
494      unw_word_t *dst, src;
495
496      /* Need to copy-in ucontext_t first.  */
497      ucp = alloca (sizeof (ucontext_t));
498      if (!ucp)
499	return -UNW_ENOMEM;
500
501      /* For now, there is no non-HP-UX implementation of the
502         uc_access(3) interface.  Because of that, we cannot, e.g.,
503         unwind an HP-UX program from a Linux program.  Should that
504         become possible at some point in the future, the
505         copy-in/copy-out needs to be adjusted to do byte-swapping if
506         necessary. */
507      assert (c->as->big_endian == (__BYTE_ORDER == __BIG_ENDIAN));
508
509      dst = (unw_word_t *) ucp;
510      for (src = uc_addr; src < uc_addr + sizeof (ucontext_t); src += 8)
511	if ((ret = (*c->as->acc.access_mem) (c->as, src, dst++, 0, c->as_arg))
512	    < 0)
513	  return ret;
514    }
515
516  if ((ret = access_fpreg (unw_local_addr_space, IA64_GET_REG (loc), valp,
517			   write, ucp)) < 0)
518    return ret;
519
520  if (write && c->as != unw_local_addr_space)
521    {
522      /* need to copy-out ucontext_t: */
523      unw_word_t dst, *src = (unw_word_t *) ucp;
524      for (dst = uc_addr; dst < uc_addr + sizeof (ucontext_t); dst += 8)
525	if ((ret = (*c->as->acc.access_mem) (c->as, dst, src++, 1, c->as_arg))
526	    < 0)
527	  return ret;
528    }
529  return 0;
530#else /* !HAVE_SYS_UC_ACCESS_H */
531  return -UNW_EINVAL;
532#endif /* !HAVE_SYS_UC_ACCESS_H */
533}
534
535#endif /* UNW_LOCAL_ONLY */
536