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))
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
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);
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