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