• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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 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   // Access the `_U_dyn_info_list` from `LOCAL_ONLY` library, i.e. libunwind.so.
72   *dyn_info_list_addr = _U_dyn_info_list_addr ();
73   return 0;
74 }
75 
76 static int
access_mem(unw_addr_space_t as,unw_word_t addr,unw_word_t * val,int write,void * arg)77 access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
78             void *arg)
79 {
80   if (write)
81     {
82       Debug (12, "mem[%lx] <- %lx\n", addr, *val);
83       *(unw_word_t *) addr = *val;
84     }
85   else
86     {
87       *val = *(unw_word_t *) addr;
88       Debug (12, "mem[%lx] -> %lx\n", addr, *val);
89     }
90   return 0;
91 }
92 
93 #ifdef HAVE_SYS_UC_ACCESS_H
94 
95 #define SYSCALL_CFM_SAVE_REG    11 /* on a syscall, ar.pfs is saved in r11 */
96 #define REASON_SYSCALL          0
97 
98 static int
access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)99 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
100             void *arg)
101 {
102   ucontext_t *uc = arg;
103   unsigned int nat, mask;
104   uint64_t value;
105   uint16_t reason;
106   int ret;
107 
108   __uc_get_reason (uc, &reason);
109 
110   switch (reg)
111     {
112     case UNW_IA64_GR  ... UNW_IA64_GR + 31:
113       if ((ret = __uc_get_grs (uc, (reg - UNW_IA64_GR), 1, &value, &nat)))
114         break;
115 
116       if (write)
117         ret = __uc_set_grs (uc, (reg - UNW_IA64_GR), 1, val, nat);
118       else
119         *val = value;
120       break;
121 
122     case UNW_IA64_NAT ... UNW_IA64_NAT + 31:
123       if ((ret = __uc_get_grs (uc, (reg - UNW_IA64_GR), 1, &value, &nat)))
124         break;
125 
126       mask = 1 << (reg - UNW_IA64_GR);
127 
128       if (write)
129         {
130           if (*val)
131             nat |= mask;
132           else
133             nat &= ~mask;
134           ret = __uc_set_grs (uc, (reg - UNW_IA64_GR), 1, &value, nat);
135         }
136       else
137         *val = (nat & mask) != 0;
138       break;
139 
140     case UNW_IA64_AR  ... UNW_IA64_AR + 127:
141       if (reg == UNW_IA64_AR_BSP)
142         {
143           if (write)
144             ret = __uc_set_ar (uc, (reg - UNW_IA64_AR), *val);
145           else
146             ret = __uc_get_ar (uc, (reg - UNW_IA64_AR), val);
147         }
148       else if (reg == UNW_IA64_AR_PFS && reason == REASON_SYSCALL)
149         {
150           /* As of HP-UX 11.22, getcontext() does not have unwind info
151              and because of that, we need to hack thins manually here.
152              Hopefully, this is OK because the HP-UX kernel also needs
153              to know where AR.PFS has been saved, so the use of
154              register r11 for this purpose is pretty much nailed
155              down.  */
156           if (write)
157             ret = __uc_set_grs (uc, SYSCALL_CFM_SAVE_REG, 1, val, 0);
158           else
159             ret = __uc_get_grs (uc, SYSCALL_CFM_SAVE_REG, 1, val, &nat);
160         }
161       else
162         {
163           if (write)
164             ret = __uc_set_ar (uc, (reg - UNW_IA64_AR), *val);
165           else
166             ret = __uc_get_ar (uc, (reg - UNW_IA64_AR), val);
167         }
168       break;
169 
170     case UNW_IA64_BR  ... UNW_IA64_BR + 7:
171       if (write)
172         ret = __uc_set_brs (uc, (reg - UNW_IA64_BR), 1, val);
173       else
174         ret = __uc_get_brs (uc, (reg - UNW_IA64_BR), 1, val);
175       break;
176 
177     case UNW_IA64_PR:
178       if (write)
179         ret = __uc_set_prs (uc, *val);
180       else
181         ret = __uc_get_prs (uc, val);
182       break;
183 
184     case UNW_IA64_IP:
185       if (write)
186         ret = __uc_set_ip (uc, *val);
187       else
188         ret = __uc_get_ip (uc, val);
189       break;
190 
191     case UNW_IA64_CFM:
192       if (write)
193         ret = __uc_set_cfm (uc, *val);
194       else
195         ret = __uc_get_cfm (uc, val);
196       break;
197 
198     case UNW_IA64_FR  ... UNW_IA64_FR + 127:
199     default:
200       ret = EINVAL;
201       break;
202     }
203 
204   if (ret != 0)
205     {
206       Debug (1, "failed to %s %s (ret = %d)\n",
207              write ? "write" : "read", unw_regname (reg), ret);
208       return -UNW_EBADREG;
209     }
210 
211   if (write)
212     Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
213   else
214     Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
215   return 0;
216 }
217 
218 static int
access_fpreg(unw_addr_space_t as,unw_regnum_t reg,unw_fpreg_t * val,int write,void * arg)219 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
220               int write, void *arg)
221 {
222   ucontext_t *uc = arg;
223   fp_regval_t fp_regval;
224   int ret;
225 
226   switch (reg)
227     {
228     case UNW_IA64_FR  ... UNW_IA64_FR + 127:
229       if (write)
230         {
231           memcpy (&fp_regval, val, sizeof (fp_regval));
232           ret = __uc_set_frs (uc, (reg - UNW_IA64_FR), 1, &fp_regval);
233         }
234       else
235         {
236           ret = __uc_get_frs (uc, (reg - UNW_IA64_FR), 1, &fp_regval);
237           memcpy (val, &fp_regval, sizeof (*val));
238         }
239       break;
240 
241     default:
242       ret = EINVAL;
243       break;
244     }
245   if (ret != 0)
246     return -UNW_EBADREG;
247 
248   return 0;
249 }
250 
251 #else /* !HAVE_SYS_UC_ACCESS_H */
252 
253 static int
access_reg(unw_addr_space_t as,unw_regnum_t reg,unw_word_t * val,int write,void * arg)254 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
255             void *arg)
256 {
257   unw_word_t *addr, mask;
258   ucontext_t *uc = arg;
259 
260   if (reg >= UNW_IA64_NAT + 4 && reg <= UNW_IA64_NAT + 7)
261     {
262       mask = ((unw_word_t) 1) << (reg - UNW_IA64_NAT);
263       if (write)
264         {
265           if (*val)
266             uc->uc_mcontext.sc_nat |= mask;
267           else
268             uc->uc_mcontext.sc_nat &= ~mask;
269         }
270       else
271         *val = (uc->uc_mcontext.sc_nat & mask) != 0;
272 
273       if (write)
274         Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
275       else
276         Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
277       return 0;
278     }
279 
280   addr = tdep_uc_addr (uc, reg, NULL);
281   if (!addr)
282     goto badreg;
283 
284   if (write)
285     {
286       if (ia64_read_only_reg (addr))
287         {
288           Debug (16, "attempt to write read-only register\n");
289           return -UNW_EREADONLYREG;
290         }
291       *addr = *val;
292       Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
293     }
294   else
295     {
296       *val = *(unw_word_t *) addr;
297       Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
298     }
299   return 0;
300 
301  badreg:
302   Debug (1, "bad register number %u\n", reg);
303   return -UNW_EBADREG;
304 }
305 
306 static int
access_fpreg(unw_addr_space_t as,unw_regnum_t reg,unw_fpreg_t * val,int write,void * arg)307 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
308               int write, void *arg)
309 {
310   ucontext_t *uc = arg;
311   unw_fpreg_t *addr;
312 
313   if (reg < UNW_IA64_FR || reg >= UNW_IA64_FR + 128)
314     goto badreg;
315 
316   addr = tdep_uc_addr (uc, reg, NULL);
317   if (!addr)
318     goto badreg;
319 
320   if (write)
321     {
322       if (ia64_read_only_reg (addr))
323         {
324           Debug (16, "attempt to write read-only register\n");
325           return -UNW_EREADONLYREG;
326         }
327       *addr = *val;
328       Debug (12, "%s <- %016lx.%016lx\n",
329              unw_regname (reg), val->raw.bits[1], val->raw.bits[0]);
330     }
331   else
332     {
333       *val = *(unw_fpreg_t *) addr;
334       Debug (12, "%s -> %016lx.%016lx\n",
335              unw_regname (reg), val->raw.bits[1], val->raw.bits[0]);
336     }
337   return 0;
338 
339  badreg:
340   Debug (1, "bad register number %u\n", reg);
341   /* attempt to access a non-preserved register */
342   return -UNW_EBADREG;
343 }
344 
345 #endif /* !HAVE_SYS_UC_ACCESS_H */
346 
347 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)348 get_static_proc_name (unw_addr_space_t as, unw_word_t ip,
349                       char *buf, size_t buf_len, unw_word_t *offp,
350                       void *arg)
351 {
352   return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
353 }
354 
355 HIDDEN void
ia64_local_addr_space_init(void)356 ia64_local_addr_space_init (void)
357 {
358   memset (&local_addr_space, 0, sizeof (local_addr_space));
359   local_addr_space.big_endian = target_is_big_endian();
360 #if defined(__linux__)
361   local_addr_space.abi = ABI_LINUX;
362 #elif defined(__hpux)
363   local_addr_space.abi = ABI_HPUX;
364 #endif
365   local_addr_space.caching_policy = UNWI_DEFAULT_CACHING_POLICY;
366   local_addr_space.acc.find_proc_info = tdep_find_proc_info;
367   local_addr_space.acc.put_unwind_info = put_unwind_info;
368   local_addr_space.acc.get_dyn_info_list_addr = get_dyn_info_list_addr;
369   local_addr_space.acc.access_mem = access_mem;
370   local_addr_space.acc.access_reg = access_reg;
371   local_addr_space.acc.access_fpreg = access_fpreg;
372   local_addr_space.acc.resume = ia64_local_resume;
373   local_addr_space.acc.get_proc_name = get_static_proc_name;
374   unw_flush_cache (&local_addr_space, 0, 0);
375 }
376 
377 #endif /* !UNW_REMOTE_ONLY */
378 
379 #ifndef UNW_LOCAL_ONLY
380 
381 HIDDEN int
ia64_uc_access_reg(struct cursor * c,ia64_loc_t loc,unw_word_t * valp,int write)382 ia64_uc_access_reg (struct cursor *c, ia64_loc_t loc, unw_word_t *valp,
383                     int write)
384 {
385 #ifdef HAVE_SYS_UC_ACCESS_H
386   unw_word_t uc_addr = IA64_GET_AUX_ADDR (loc);
387   ucontext_t *ucp;
388   int ret;
389 
390   Debug (16, "%s location %s\n",
391          write ? "writing" : "reading", ia64_strloc (loc));
392 
393   if (c->as == unw_local_addr_space)
394     ucp = (ucontext_t *) uc_addr;
395   else
396     {
397       unw_word_t *dst, src;
398 
399       /* Need to copy-in ucontext_t first.  */
400       ucp = alloca (sizeof (ucontext_t));
401       if (!ucp)
402         return -UNW_ENOMEM;
403 
404       /* For now, there is no non-HP-UX implementation of the
405          uc_access(3) interface.  Because of that, we cannot, e.g.,
406          unwind an HP-UX program from a Linux program.  Should that
407          become possible at some point in the future, the
408          copy-in/copy-out needs to be adjusted to do byte-swapping if
409          necessary. */
410       assert (c->as->big_endian == target_is_big_endian());
411 
412       dst = (unw_word_t *) ucp;
413       for (src = uc_addr; src < uc_addr + sizeof (ucontext_t); src += 8)
414         if ((ret = (*c->as->acc.access_mem) (c->as, src, dst++, 0, c->as_arg))
415             < 0)
416           return ret;
417     }
418 
419   if (IA64_IS_REG_LOC (loc))
420     ret = access_reg (unw_local_addr_space, IA64_GET_REG (loc), valp, write,
421                       ucp);
422   else
423     {
424       /* Must be an access to the RSE backing store in ucontext_t.  */
425       unw_word_t addr = IA64_GET_ADDR (loc);
426 
427       if (write)
428         ret = __uc_set_rsebs (ucp, (uint64_t *) addr, 1, valp);
429       else
430         ret = __uc_get_rsebs (ucp, (uint64_t *) addr, 1, valp);
431       if (ret != 0)
432         ret = -UNW_EBADREG;
433     }
434   if (ret < 0)
435     return ret;
436 
437   if (write && c->as != unw_local_addr_space)
438     {
439       /* need to copy-out ucontext_t: */
440       unw_word_t dst, *src = (unw_word_t *) ucp;
441       for (dst = uc_addr; dst < uc_addr + sizeof (ucontext_t); dst += 8)
442         if ((ret = (*c->as->acc.access_mem) (c->as, dst, src++, 1, c->as_arg))
443             < 0)
444           return ret;
445     }
446   return 0;
447 #else /* !HAVE_SYS_UC_ACCESS_H */
448   return -UNW_EINVAL;
449 #endif /* !HAVE_SYS_UC_ACCESS_H */
450 }
451 
452 HIDDEN int
ia64_uc_access_fpreg(struct cursor * c,ia64_loc_t loc,unw_fpreg_t * valp,int write)453 ia64_uc_access_fpreg (struct cursor *c, ia64_loc_t loc, unw_fpreg_t *valp,
454                       int write)
455 {
456 #ifdef HAVE_SYS_UC_ACCESS_H
457   unw_word_t uc_addr = IA64_GET_AUX_ADDR (loc);
458   ucontext_t *ucp;
459   int ret;
460 
461   if (c->as == unw_local_addr_space)
462     ucp = (ucontext_t *) uc_addr;
463   else
464     {
465       unw_word_t *dst, src;
466 
467       /* Need to copy-in ucontext_t first.  */
468       ucp = alloca (sizeof (ucontext_t));
469       if (!ucp)
470         return -UNW_ENOMEM;
471 
472       /* For now, there is no non-HP-UX implementation of the
473          uc_access(3) interface.  Because of that, we cannot, e.g.,
474          unwind an HP-UX program from a Linux program.  Should that
475          become possible at some point in the future, the
476          copy-in/copy-out needs to be adjusted to do byte-swapping if
477          necessary. */
478       assert (c->as->big_endian == target_is_big_endian());
479 
480       dst = (unw_word_t *) ucp;
481       for (src = uc_addr; src < uc_addr + sizeof (ucontext_t); src += 8)
482         if ((ret = (*c->as->acc.access_mem) (c->as, src, dst++, 0, c->as_arg))
483             < 0)
484           return ret;
485     }
486 
487   if ((ret = access_fpreg (unw_local_addr_space, IA64_GET_REG (loc), valp,
488                            write, ucp)) < 0)
489     return ret;
490 
491   if (write && c->as != unw_local_addr_space)
492     {
493       /* need to copy-out ucontext_t: */
494       unw_word_t dst, *src = (unw_word_t *) ucp;
495       for (dst = uc_addr; dst < uc_addr + sizeof (ucontext_t); dst += 8)
496         if ((ret = (*c->as->acc.access_mem) (c->as, dst, src++, 1, c->as_arg))
497             < 0)
498           return ret;
499     }
500   return 0;
501 #else /* !HAVE_SYS_UC_ACCESS_H */
502   return -UNW_EINVAL;
503 #endif /* !HAVE_SYS_UC_ACCESS_H */
504 }
505 
506 #endif /* UNW_LOCAL_ONLY */
507