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 
6 This file is part of libunwind.
7 
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:
15 
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
18 
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.  */
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.  */
36 PROTECTED unw_addr_space_t unw_local_addr_space;
37 
38 #else /* !UNW_REMOTE_ONLY */
39 
40 static struct unw_addr_space local_addr_space;
41 
42 PROTECTED 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 
48 HIDDEN void *
tdep_uc_addr(ucontext_t * uc,int reg,uint8_t * nat_bitnr)49 tdep_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 
56 static void
put_unwind_info(unw_addr_space_t as,unw_proc_info_t * proc_info,void * arg)57 put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
58 {
59   /* it's a no-op */
60 }
61 
62 static int
get_dyn_info_list_addr(unw_addr_space_t as,unw_word_t * dyn_info_list_addr,void * arg)63 get_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 
75 static int
access_mem(unw_addr_space_t as,unw_word_t addr,unw_word_t * val,int write,void * arg)76 access_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, sizeof(unw_word_t)))
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, sizeof(unw_word_t)))
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 
125 static int
access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)126 access_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 
245 static int
access_fpreg(unw_addr_space_t as,unw_regnum_t reg,unw_fpreg_t * val,int write,void * arg)246 access_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 
280 static int
access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)281 access_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 
333 static int
access_fpreg(unw_addr_space_t as,unw_regnum_t reg,unw_fpreg_t * val,int write,void * arg)334 access_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 
374 static int
get_static_proc_name(unw_addr_space_t as,unw_word_t ip,char * buf,size_t buf_len,unw_word_t * offp,void * arg)375 get_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, arg);
380 }
381 
382 HIDDEN void
ia64_local_addr_space_init(void)383 ia64_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 
410 HIDDEN int
ia64_uc_access_reg(struct cursor * c,ia64_loc_t loc,unw_word_t * valp,int write)411 ia64_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 
481 HIDDEN int
ia64_uc_access_fpreg(struct cursor * c,ia64_loc_t loc,unw_fpreg_t * valp,int write)482 ia64_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