vfs: check userland buffers before reading them.
[haiku.git] / src / libs / libunwind / ia64 / Ginit.c
blob7b64f0c1e1e94f9d5f333b9cfb857a1964d64a45
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>
6 This file is part of libunwind.
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:
16 The above copyright notice and this permission notice shall be
17 included in all copies or substantial portions of the Software.
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. */
27 #include "unwind_i.h"
29 #ifdef HAVE_SYS_UC_ACCESS_H
30 # include <sys/uc_access.h>
31 #endif
33 #ifdef UNW_REMOTE_ONLY
35 /* unw_local_addr_space is a NULL pointer in this case. */
36 PROTECTED unw_addr_space_t unw_local_addr_space;
38 #else /* !UNW_REMOTE_ONLY */
40 static struct unw_addr_space local_addr_space;
42 PROTECTED unw_addr_space_t unw_local_addr_space = &local_addr_space;
44 #ifdef HAVE_SYS_UC_ACCESS_H
46 #else /* !HAVE_SYS_UC_ACCESS_H */
48 HIDDEN void *
49 tdep_uc_addr (ucontext_t *uc, int reg, uint8_t *nat_bitnr)
51 return inlined_uc_addr (uc, reg, nat_bitnr);
54 #endif /* !HAVE_SYS_UC_ACCESS_H */
56 static void
57 put_unwind_info (unw_addr_space_t as, unw_proc_info_t *proc_info, void *arg)
59 /* it's a no-op */
62 static int
63 get_dyn_info_list_addr (unw_addr_space_t as, unw_word_t *dyn_info_list_addr,
64 void *arg)
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;
75 static int
76 access_mem (unw_addr_space_t as, unw_word_t addr, unw_word_t *val, int write,
77 void *arg)
79 if (write)
81 Debug (12, "mem[%lx] <- %lx\n", addr, *val);
82 *(unw_word_t *) addr = *val;
84 else
86 *val = *(unw_word_t *) addr;
87 Debug (12, "mem[%lx] -> %lx\n", addr, *val);
89 return 0;
92 #ifdef HAVE_SYS_UC_ACCESS_H
94 #define SYSCALL_CFM_SAVE_REG 11 /* on a syscall, ar.pfs is saved in r11 */
95 #define REASON_SYSCALL 0
97 static int
98 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
99 void *arg)
101 ucontext_t *uc = arg;
102 unsigned int nat, mask;
103 uint64_t value;
104 uint16_t reason;
105 int ret;
107 __uc_get_reason (uc, &reason);
109 switch (reg)
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;
115 if (write)
116 ret = __uc_set_grs (uc, (reg - UNW_IA64_GR), 1, val, nat);
117 else
118 *val = value;
119 break;
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;
125 mask = 1 << (reg - UNW_IA64_GR);
127 if (write)
129 if (*val)
130 nat |= mask;
131 else
132 nat &= ~mask;
133 ret = __uc_set_grs (uc, (reg - UNW_IA64_GR), 1, &value, nat);
135 else
136 *val = (nat & mask) != 0;
137 break;
139 case UNW_IA64_AR ... UNW_IA64_AR + 127:
140 if (reg == UNW_IA64_AR_BSP)
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);
147 else if (reg == UNW_IA64_AR_PFS && reason == REASON_SYSCALL)
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);
160 else
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);
167 break;
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;
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;
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;
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;
197 case UNW_IA64_FR ... UNW_IA64_FR + 127:
198 default:
199 ret = EINVAL;
200 break;
203 if (ret != 0)
205 Debug (1, "failed to %s %s (ret = %d)\n",
206 write ? "write" : "read", unw_regname (reg), ret);
207 return -UNW_EBADREG;
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;
217 static int
218 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
219 int write, void *arg)
221 ucontext_t *uc = arg;
222 fp_regval_t fp_regval;
223 int ret;
225 switch (reg)
227 case UNW_IA64_FR ... UNW_IA64_FR + 127:
228 if (write)
230 memcpy (&fp_regval, val, sizeof (fp_regval));
231 ret = __uc_set_frs (uc, (reg - UNW_IA64_FR), 1, &fp_regval);
233 else
235 ret = __uc_get_frs (uc, (reg - UNW_IA64_FR), 1, &fp_regval);
236 memcpy (val, &fp_regval, sizeof (*val));
238 break;
240 default:
241 ret = EINVAL;
242 break;
244 if (ret != 0)
245 return -UNW_EBADREG;
247 return 0;
250 #else /* !HAVE_SYS_UC_ACCESS_H */
252 static int
253 access_reg (unw_addr_space_t as, unw_regnum_t reg, unw_word_t *val, int write,
254 void *arg)
256 unw_word_t *addr, mask;
257 ucontext_t *uc = arg;
259 if (reg >= UNW_IA64_NAT + 4 && reg <= UNW_IA64_NAT + 7)
261 mask = ((unw_word_t) 1) << (reg - UNW_IA64_NAT);
262 if (write)
264 if (*val)
265 uc->uc_mcontext.sc_nat |= mask;
266 else
267 uc->uc_mcontext.sc_nat &= ~mask;
269 else
270 *val = (uc->uc_mcontext.sc_nat & mask) != 0;
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;
279 addr = tdep_uc_addr (uc, reg, NULL);
280 if (!addr)
281 goto badreg;
283 if (write)
285 if (ia64_read_only_reg (addr))
287 Debug (16, "attempt to write read-only register\n");
288 return -UNW_EREADONLYREG;
290 *addr = *val;
291 Debug (12, "%s <- %lx\n", unw_regname (reg), *val);
293 else
295 *val = *(unw_word_t *) addr;
296 Debug (12, "%s -> %lx\n", unw_regname (reg), *val);
298 return 0;
300 badreg:
301 Debug (1, "bad register number %u\n", reg);
302 return -UNW_EBADREG;
305 static int
306 access_fpreg (unw_addr_space_t as, unw_regnum_t reg, unw_fpreg_t *val,
307 int write, void *arg)
309 ucontext_t *uc = arg;
310 unw_fpreg_t *addr;
312 if (reg < UNW_IA64_FR || reg >= UNW_IA64_FR + 128)
313 goto badreg;
315 addr = tdep_uc_addr (uc, reg, NULL);
316 if (!addr)
317 goto badreg;
319 if (write)
321 if (ia64_read_only_reg (addr))
323 Debug (16, "attempt to write read-only register\n");
324 return -UNW_EREADONLYREG;
326 *addr = *val;
327 Debug (12, "%s <- %016lx.%016lx\n",
328 unw_regname (reg), val->raw.bits[1], val->raw.bits[0]);
330 else
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]);
336 return 0;
338 badreg:
339 Debug (1, "bad register number %u\n", reg);
340 /* attempt to access a non-preserved register */
341 return -UNW_EBADREG;
344 #endif /* !HAVE_SYS_UC_ACCESS_H */
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)
351 return _Uelf64_get_proc_name (as, getpid (), ip, buf, buf_len, offp);
354 HIDDEN void
355 ia64_local_addr_space_init (void)
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);
376 #endif /* !UNW_REMOTE_ONLY */
378 #ifndef UNW_LOCAL_ONLY
380 HIDDEN int
381 ia64_uc_access_reg (struct cursor *c, ia64_loc_t loc, unw_word_t *valp,
382 int write)
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;
389 Debug (16, "%s location %s\n",
390 write ? "writing" : "reading", ia64_strloc (loc));
392 if (c->as == unw_local_addr_space)
393 ucp = (ucontext_t *) uc_addr;
394 else
396 unw_word_t *dst, src;
398 /* Need to copy-in ucontext_t first. */
399 ucp = alloca (sizeof (ucontext_t));
400 if (!ucp)
401 return -UNW_ENOMEM;
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));
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;
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
423 /* Must be an access to the RSE backing store in ucontext_t. */
424 unw_word_t addr = IA64_GET_ADDR (loc);
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;
433 if (ret < 0)
434 return ret;
436 if (write && c->as != unw_local_addr_space)
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;
445 return 0;
446 #else /* !HAVE_SYS_UC_ACCESS_H */
447 return -UNW_EINVAL;
448 #endif /* !HAVE_SYS_UC_ACCESS_H */
451 HIDDEN int
452 ia64_uc_access_fpreg (struct cursor *c, ia64_loc_t loc, unw_fpreg_t *valp,
453 int write)
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;
460 if (c->as == unw_local_addr_space)
461 ucp = (ucontext_t *) uc_addr;
462 else
464 unw_word_t *dst, src;
466 /* Need to copy-in ucontext_t first. */
467 ucp = alloca (sizeof (ucontext_t));
468 if (!ucp)
469 return -UNW_ENOMEM;
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));
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;
486 if ((ret = access_fpreg (unw_local_addr_space, IA64_GET_REG (loc), valp,
487 write, ucp)) < 0)
488 return ret;
490 if (write && c->as != unw_local_addr_space)
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;
499 return 0;
500 #else /* !HAVE_SYS_UC_ACCESS_H */
501 return -UNW_EINVAL;
502 #endif /* !HAVE_SYS_UC_ACCESS_H */
505 #endif /* UNW_LOCAL_ONLY */