5afa9ab19b2cfcc31fb53a4405be575915dd5f0c
[platform/upstream/libunwind.git] / src / ia64 / Ginit.c
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 *
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
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
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
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       Debug (12, "mem[%lx] <- %lx\n", addr, *val);
82       *(unw_word_t *) addr = *val;
83     }
84   else
85     {
86       *val = *(unw_word_t *) addr;
87       Debug (12, "mem[%lx] -> %lx\n", addr, *val);
88     }
89   return 0;
90 }
91
92 #ifdef HAVE_SYS_UC_ACCESS_H
93
94 #define SYSCALL_CFM_SAVE_REG    11 /* on a syscall, ar.pfs is saved in r11 */
95 #define REASON_SYSCALL          0
96
97 static int
98 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
99             void *arg)
100 {
101   ucontext_t *uc = arg;
102   unsigned int nat, mask;
103   uint64_t value;
104   uint16_t reason;
105   int ret;
106
107   __uc_get_reason (uc, &reason);
108
109   switch (reg)
110     {
111     case UNW_IA64_GR  ... UNW_IA64_GR + 31:
112       if ((ret = __uc_get_grs (uc, (reg - UNW_IA64_GR), 1, &value, &nat)))
113         break;
114
115       if (write)
116         ret = __uc_set_grs (uc, (reg - UNW_IA64_GR), 1, val, nat);
117       else
118         *val = value;
119       break;
120
121     case UNW_IA64_NAT ... UNW_IA64_NAT + 31:
122       if ((ret = __uc_get_grs (uc, (reg - UNW_IA64_GR), 1, &value, &nat)))
123         break;
124
125       mask = 1 << (reg - UNW_IA64_GR);
126
127       if (write)
128         {
129           if (*val)
130             nat |= mask;
131           else
132             nat &= ~mask;
133           ret = __uc_set_grs (uc, (reg - UNW_IA64_GR), 1, &value, nat);
134         }
135       else
136         *val = (nat & mask) != 0;
137       break;
138
139     case UNW_IA64_AR  ... UNW_IA64_AR + 127:
140       if (reg == UNW_IA64_AR_BSP)
141         {
142           if (write)
143             ret = __uc_set_ar (uc, (reg - UNW_IA64_AR), *val);
144           else
145             ret = __uc_get_ar (uc, (reg - UNW_IA64_AR), val);
146         }
147       else if (reg == UNW_IA64_AR_PFS && reason == REASON_SYSCALL)
148         {
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
154              down.  */
155           if (write)
156             ret = __uc_set_grs (uc, SYSCALL_CFM_SAVE_REG, 1, val, 0);
157           else
158             ret = __uc_get_grs (uc, SYSCALL_CFM_SAVE_REG, 1, val, &nat);
159         }
160       else
161         {
162           if (write)
163             ret = __uc_set_ar (uc, (reg - UNW_IA64_AR), *val);
164           else
165             ret = __uc_get_ar (uc, (reg - UNW_IA64_AR), val);
166         }
167       break;
168
169     case UNW_IA64_BR  ... UNW_IA64_BR + 7:
170       if (write)
171         ret = __uc_set_brs (uc, (reg - UNW_IA64_BR), 1, val);
172       else
173         ret = __uc_get_brs (uc, (reg - UNW_IA64_BR), 1, val);
174       break;
175
176     case UNW_IA64_PR:
177       if (write)
178         ret = __uc_set_prs (uc, *val);
179       else
180         ret = __uc_get_prs (uc, val);
181       break;
182
183     case UNW_IA64_IP:
184       if (write)
185         ret = __uc_set_ip (uc, *val);
186       else
187         ret = __uc_get_ip (uc, val);
188       break;
189
190     case UNW_IA64_CFM:
191       if (write)
192         ret = __uc_set_cfm (uc, *val);
193       else
194         ret = __uc_get_cfm (uc, val);
195       break;
196
197     case UNW_IA64_FR  ... UNW_IA64_FR + 127:
198     default:
199       ret = EINVAL;
200       break;
201     }
202
203   if (ret != 0)
204     {
205       Debug (1, "failed to %s %s (ret = %d)\n",
206              write ? "write" : "read", unw_regname (reg), ret);
207       return -UNW_EBADREG;
208     }
209
210   if (write)
211     Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
212   else
213     Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
214   return 0;
215 }
216
217 static int
218 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
219               int write, void *arg)
220 {
221   ucontext_t *uc = arg;
222   fp_regval_t fp_regval;
223   int ret;
224
225   switch (reg)
226     {
227     case UNW_IA64_FR  ... UNW_IA64_FR + 127:
228       if (write)
229         {
230           memcpy (&fp_regval, val, sizeof (fp_regval));
231           ret = __uc_set_frs (uc, (reg - UNW_IA64_FR), 1, &fp_regval);
232         }
233       else
234         {
235           ret = __uc_get_frs (uc, (reg - UNW_IA64_FR), 1, &fp_regval);
236           memcpy (val, &fp_regval, sizeof (*val));
237         }
238       break;
239
240     default:
241       ret = EINVAL;
242       break;
243     }
244   if (ret != 0)
245     return -UNW_EBADREG;
246
247   return 0;
248 }
249
250 #else /* !HAVE_SYS_UC_ACCESS_H */
251
252 static int
253 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
254             void *arg)
255 {
256   unw_word_t *addr, mask;
257   ucontext_t *uc = arg;
258
259   if (reg >= UNW_IA64_NAT + 4 && reg <= UNW_IA64_NAT + 7)
260     {
261       mask = ((unw_word_t) 1) << (reg - UNW_IA64_NAT);
262       if (write)
263         {
264           if (*val)
265             uc->uc_mcontext.sc_nat |= mask;
266           else
267             uc->uc_mcontext.sc_nat &= ~mask;
268         }
269       else
270         *val = (uc->uc_mcontext.sc_nat & mask) != 0;
271
272       if (write)
273         Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
274       else
275         Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
276       return 0;
277     }
278
279   addr = tdep_uc_addr (uc, reg, NULL);
280   if (!addr)
281     goto badreg;
282
283   if (write)
284     {
285       if (ia64_read_only_reg (addr))
286         {
287           Debug (16, "attempt to write read-only register\n");
288           return -UNW_EREADONLYREG;
289         }
290       *addr = *val;
291       Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
292     }
293   else
294     {
295       *val = *(unw_word_t *) addr;
296       Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
297     }
298   return 0;
299
300  badreg:
301   Debug (1, "bad register number %u\n", reg);
302   return -UNW_EBADREG;
303 }
304
305 static int
306 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
307               int write, void *arg)
308 {
309   ucontext_t *uc = arg;
310   unw_fpreg_t *addr;
311
312   if (reg < UNW_IA64_FR || reg >= UNW_IA64_FR + 128)
313     goto badreg;
314
315   addr = tdep_uc_addr (uc, reg, NULL);
316   if (!addr)
317     goto badreg;
318
319   if (write)
320     {
321       if (ia64_read_only_reg (addr))
322         {
323           Debug (16, "attempt to write read-only register\n");
324           return -UNW_EREADONLYREG;
325         }
326       *addr = *val;
327       Debug (12, "%s <- %016lx.%016lx\n",
328              unw_regname (reg), val->raw.bits[1], val->raw.bits[0]);
329     }
330   else
331     {
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]);
335     }
336   return 0;
337
338  badreg:
339   Debug (1, "bad register number %u\n", reg);
340   /* attempt to access a non-preserved register */
341   return -UNW_EBADREG;
342 }
343
344 #endif /* !HAVE_SYS_UC_ACCESS_H */
345
346 static int
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,
349                       void *arg)
350 {
351   return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
352 }
353
354 HIDDEN void
355 ia64_local_addr_space_init (void)
356 {
357   memset (&local_addr_space, 0, sizeof (local_addr_space));
358   local_addr_space.big_endian = (__BYTE_ORDER == __BIG_ENDIAN);
359 #if defined(__linux)
360   local_addr_space.abi = ABI_LINUX;
361 #elif defined(__hpux)
362   local_addr_space.abi = ABI_HPUX;
363 #endif
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);
374 }
375
376 #endif /* !UNW_REMOTE_ONLY */
377
378 #ifndef UNW_LOCAL_ONLY
379
380 HIDDEN int
381 ia64_uc_access_reg (struct cursor *c, ia64_loc_t loc, unw_word_t *valp,
382                     int write)
383 {
384 #ifdef HAVE_SYS_UC_ACCESS_H
385   unw_word_t uc_addr = IA64_GET_AUX_ADDR (loc);
386   ucontext_t *ucp;
387   int ret;
388
389   Debug (16, "%s location %s\n",
390          write ? "writing" : "reading", ia64_strloc (loc));
391
392   if (c->as == unw_local_addr_space)
393     ucp = (ucontext_t *) uc_addr;
394   else
395     {
396       unw_word_t *dst, src;
397
398       /* Need to copy-in ucontext_t first.  */
399       ucp = alloca (sizeof (ucontext_t));
400       if (!ucp)
401         return -UNW_ENOMEM;
402
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
408          necessary. */
409       assert (c->as->big_endian == (__BYTE_ORDER == __BIG_ENDIAN));
410
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))
414             < 0)
415           return ret;
416     }
417
418   if (IA64_IS_REG_LOC (loc))
419     ret = access_reg (unw_local_addr_space, IA64_GET_REG (loc), valp, write,
420                       ucp);
421   else
422     {
423       /* Must be an access to the RSE backing store in ucontext_t.  */
424       unw_word_t addr = IA64_GET_ADDR (loc);
425
426       if (write)
427         ret = __uc_set_rsebs (ucp, (uint64_t *) addr, 1, valp);
428       else
429         ret = __uc_get_rsebs (ucp, (uint64_t *) addr, 1, valp);
430       if (ret != 0)
431         ret = -UNW_EBADREG;
432     }
433   if (ret < 0)
434     return ret;
435
436   if (write && c->as != unw_local_addr_space)
437     {
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))
442             < 0)
443           return ret;
444     }
445   return 0;
446 #else /* !HAVE_SYS_UC_ACCESS_H */
447   return -UNW_EINVAL;
448 #endif /* !HAVE_SYS_UC_ACCESS_H */
449 }
450
451 HIDDEN int
452 ia64_uc_access_fpreg (struct cursor *c, ia64_loc_t loc, unw_fpreg_t *valp,
453                       int write)
454 {
455 #ifdef HAVE_SYS_UC_ACCESS_H
456   unw_word_t uc_addr = IA64_GET_AUX_ADDR (loc);
457   ucontext_t *ucp;
458   int ret;
459
460   if (c->as == unw_local_addr_space)
461     ucp = (ucontext_t *) uc_addr;
462   else
463     {
464       unw_word_t *dst, src;
465
466       /* Need to copy-in ucontext_t first.  */
467       ucp = alloca (sizeof (ucontext_t));
468       if (!ucp)
469         return -UNW_ENOMEM;
470
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
476          necessary. */
477       assert (c->as->big_endian == (__BYTE_ORDER == __BIG_ENDIAN));
478
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))
482             < 0)
483           return ret;
484     }
485
486   if ((ret = access_fpreg (unw_local_addr_space, IA64_GET_REG (loc), valp,
487                            write, ucp)) < 0)
488     return ret;
489
490   if (write && c->as != unw_local_addr_space)
491     {
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))
496             < 0)
497           return ret;
498     }
499   return 0;
500 #else /* !HAVE_SYS_UC_ACCESS_H */
501   return -UNW_EINVAL;
502 #endif /* !HAVE_SYS_UC_ACCESS_H */
503 }
504
505 #endif /* UNW_LOCAL_ONLY */